Exit Function

 

I have created this exit function to make an EA close whatever positions there when an order of the opposite direction is opened by the same EA.

void ClosePosition(int OP)
{
   for (int i = 0; i < OrdersTotal(); i++)
   {
      OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
      if (OrderType() <= OP_SELL && OrderType() != OP && OrderSymbol() == Symbol())
      {
         if (OrderType() == OP_BUY) OrderClose(OrderTicket(), OrderLots(), Bid, 3, Pink);
         else OrderClose(OrderTicket(), OrderLots(), Ask, 3, Lime);
      }
   }
}

In my Entry code I am calling this function like this:

//Entry Code:

if (...)
{
   open a long position;
   ClosePosition(OP_BUY);
}
else if (...)
{
   open a short position;
   ClosePosition(OP_SELL);
}

I have been running this EA live to test this function, and noticed that in case we get more than one position of the same direction opened (e.g short positions), when a position of the opposite direction (e.g long position) comes, it does not sometimes closed all the short positions (in our example). So in case we have 4 short position, when a long position comes, it may close 2 of them only. Then when another long comes, it may close another short. After that, a new short may come after these 2 longs, so the last short of the first 4 will remain open as of this time!

And interestingly, in this example, first long which closes 2 short, it does not do that necessarily for the first 2 of the 4 shorts. It may close ticket # 1 and 3, and keeps # 2 and 4, Which means that the loop is somehow skipping orders!

Can someone fix this code?

Thanks a lot,

tapo

 
 
tapo:

So in case we have 4 short position, when a long position comes, it may close 2 of them only. [...]

This is a very common question, and it's perhaps time for another full explanation rather than just pointing you towards one of the 1,432 existing topics about it on the forum.

Let's take a scenario where you have 4 sell orders open; you are about to place a buy order; and therefore all 4 existing orders should be closed. What happens in your ClosePosition() function is as follows:

* Let's call the open orders A, B, C and D. At the beginning, A is at position #0 (i.e. OrderSelect(0, SELECT_BY_POS)), B is at position #1 etc

* You select order A, at position #0, and close it. This should succeed.

* OrdersTotal() decreases from 4 to 3. Order B moves to position 0, C moves to position 1 etc

* The loop counter i increases from 0 to 1

* Therefore, on the next pass round the loop the code selects and closes order C which is now at position #1

* Order B is missed out. Similarly, order D will also be missed because after C is closed, OrdersTotal() decreases from 3 to 2, and the variable i is increased from 1 to 2 and the loop ends.

The way to fix this is very simple: loop from OrdersTotal() downwards, not from 0 upwards. In other words: for (int i = OrdersTotal() - 1; i >= 0; i--)

 

Hello jjc, your explanation is full and made sense right away :) thanks a lot.

 
  1. Not adjusting for 4/5 digit brokers (TP, SL, AND SLIPPAGE)
  2. Not using magic numbers - incompatible with other EAs
  3. Not filtering by pair - incompatible with itself on other charts
  4. Not counting down.
  5. Not checking return codes
  6. Not use this
//++++ These are adjusted for 5 digit brokers.
int     pips2points;    // slippage  3 pips    3=points    30=points
double  pips2dbl;       // Stoploss 15 pips    0.015      0.0150
int     Digits.pips;    // DoubleToStr(dbl/pips2dbl, Digits.pips)
int     init(){
     if (Digits % 2 == 1){      // DE30=1/JPY=3/EURUSD=5 https://www.mql5.com/en/forum/135345
                pips2dbl    = Point*10; pips2points = 10;   Digits.pips = 1;
    } else {    pips2dbl    = Point;    pips2points =  1;   Digits.pips = 0; }
    // OrderSend(... Slippage.Pips * pips2points, Bid - StopLossPips * pips2dbl
//---- These are adjusted for 5 digit brokers.
}
void ClosePosition(int OP)
{
    for(iPos = OrdersTotal()-1; iPos >= 0 ; iPos--) if (
        OrderSelect(iPos, SELECT_BY_POS)                // Only my orders w/
    &&  OrderMagicNumber()  == magic.number             // my magic number
    &&  OrderSymbol()       == Symbol()                 // and my pair.
    &&  OrderType() <= OP_SELL && OrderType() != OP
    ){
        if (OrderType() == OP_BUY) color clr = Pink; else clr = Lime
        if (!OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 3*pips2dbl, clr))
           Alert("OrderClose failed: ", GetLastError());
}   }
 
WHRoeder:
  1. Not adjusting for 4/5 digit brokers (TP, SL, AND SLIPPAGE)
  2. Not using magic numbers - incompatible with other EAs
  3. Not filtering by pair - incompatible with itself on other charts
  4. Not counting down.
  5. Not checking return codes
  6. Not use this


Hi WHRoeder,

I believe I had the filtering by pair, and I had added the magic # when I saw Roptor's post in the thread to which he referred me. Can you explain your code a little bit? especially this line

if (!OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 3*pips2dbl, clr))
           Alert("OrderClose failed: ", GetLastError());

Thank you,

 

Now, I've got this this single code and been running since then on a 1-min chart it in 2 separate platforms of 2 separate brokers, one is 4-digit and the other is 5.

void ClosePosition(int OP)
{
   for (int i = OrdersTotal() - 1; i >= 0; i--)
   {
      OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
      if (OrderType() <= OP_SELL && OrderType() != OP && OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
      {
         if (OrderType() == OP_BUY) OrderClose(OrderTicket(), OrderLots(), Bid, 3, Pink);
         else OrderClose(OrderTicket(), OrderLots(), Ask, 3, Lime);
      }
   }
}

It is not only that I have not faced any issue with the 5-digit platform where every function has been working just as per expected, but surprisingly there has been an issue with the 4-digit platform. It's just the other way round I guess.

E.g I had 8 long positions and 1 short carried on from last week. The short is the last which was opened on Friday, which means the code was not able to close the 8 longs. I think they were more that 8 and it closed some of them. At the beginning of trading this week, the EA opened a second short which did not close any long. Then it opened a third short which successfully killed all the 8 longs.

From the top of my head, I thought of slippage, so I increased it from 3 to 5, and now it is running. We will see what will happen later on. But I'd like to find out what the error was for not working every time. Can you tell me what line of code we can add to this function to get the error in case it fails to close a position.

Thank you a lot.

 
tapo:

Can you tell me what line of code we can add to this function to get the error in case it fails to close a position.

Does your OrderSelect work ? it returns a bool to tell you if it has worked . . . test that bool and take appropriate action . . .
Regarding OrderClose: https://docs.mql4.com/trading/OrderClose as you can see it too returns a bool, "If the function succeeds, the return value is true." so if it's false you need to do something . . for example, print all the relevant info so you can debug the issue after the event.
 
tapo:

From the top of my head, I thought of slippage, so I increased it from 3 to 5, and now it is running. [...]

That's an indirect solution to the probable problem.

The MT4 variables Ask and Bid are initialised at the beginning of a call to start() and do not then change during start() unless you explicitly call RefreshRates(). If you do something time-consuming in start() - such as closing orders - then the Ask and Bid prices can become stale, and you can find yourself trying to close orders at a price which is no longer valid. Widening your slippage tolerance is one answer to this. Updating the prices would be a better answer.

What you can do instead of using RefreshRates() is to use OrderClosePrice() as the price parameter for OrderClose(), instead of Ask/Bid. This also avoids the need to have separate lines of code for handling buys and sells (unless you really need different lime/pink marker colours). The OrderClosePrice() is updated whenever you do an OrderSelect(), without needing a separate use of RefreshRates().

As a further alternative, using MarketInfo(Symbol(), MODE_ASK) and MarketInfo(Symbol(), MODE_BID) always returns the latest available prices, equivalent to calling RefreshRates() and then using Ask or Bid.
 
jjc:
That's an indirect solution to the probable problem.

The MT4 variables Ask and Bid are initialised at the beginning of a call to start() and do not then change during start() unless you explicitly call RefreshRates(). If you do something time-consuming in start() - such as closing orders - then the Ask and Bid prices can become stale, and you can find yourself trying to close orders at a price which is no longer valid. Widening your slippage tolerance is one answer to this. Updating the prices would be a better answer.

What you can do instead of using RefreshRates() is to use OrderClosePrice() as the price parameter for OrderClose(), instead of Ask/Bid. This also avoids the need to have separate lines of code for handling buys and sells (unless you really need different lime/pink marker colours). The OrderClosePrice() is updated whenever you do an OrderSelect(), without needing a separate use of RefreshRates().

As a further alternative, using MarketInfo(Symbol(), MODE_ASK) and MarketInfo(Symbol(), MODE_BID) always returns the latest available prices, equivalent to calling RefreshRates() and then using Ask or Bid.

jjc, thank you very much for the explanation. I don't really need different colours. I've changed the code in the 5-digit platform (which has no issue so far) to this single line

OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 3, Yellow);

Should it do the same job?

Raptor, can you tell how you can go about the coding? Would you write it like this?

void ClosePosition(int OP)
{
   for (int i = OrdersTotal() - 1; i >= 0; i--)
   {
      OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
      if (OrderType() <= OP_SELL && OrderType() != OP && OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
      {
         if (OrderType() == OP_BUY)
         {
            OrderClose(OrderTicket(), OrderLots(), Bid, 3, Pink);
            if (!OrderClose(OrderTicket(), OrderLots(), Bid, 3, Pink)) Print("...", GetLastError());
         }
         else
         {
            OrderClose(OrderTicket(), OrderLots(), Ask, 3, Lime);
            if (!OrderClose(OrderTicket(), OrderLots(), Ask, 3, Lime)) Print("...", GetLastError());
         }
      }
   }
}
 
tapo:

Raptor, can you tell how you can go about the coding? Would you write it like this?

Not far off . . . but you have 2 OrderClose calls there . . . you just need one of them, and add in a little more info to the print . .

 OrderClose(OrderTicket(), OrderLots(), Ask, 3, Lime);  //  <--------- delete this line

   if (!OrderClose(OrderTicket(), OrderLots(), Ask, 3, Lime)) Print("OrderClose failed, order no. ",OrderTicket(), " Error: ", GetLastError());
Reason: