Trying to program a function that closes half a position then moves the stop loss to break even (MQL4)

 

So for my strategy I want to close half of the open position when it hits a TP1 value and then I want the remaining order to be modified so that the new stop loss is set at the open price. The code below is what I have, but I'm not sure how to make it work using the OrderSelect and OrderModify functions. 

void CloseHalf(int Command){
   double ClosePrice=0;
   for( int i = 0 ; i < OrdersTotal() ; i++ ) {
      if( OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) == false ) {
         Print("ERROR - Unable to select the order - ",GetLastError());
         break;
      }
      if( OrderSymbol()==Symbol() && OrderType()==Command && OrderLots()==LotSize) {
         if(Command==OP_BUY) ClosePrice=Bid;
         if(Command==OP_SELL) ClosePrice=Ask;
         double Lots=OrderLots();
         int Ticket=OrderTicket();
         for(int j=1; j<OrderOpRetry; j++){
            bool res=OrderClose(Ticket,Lots/2,ClosePrice,Slip,Red);
            if(res){
               Print("TRADE - CLOSE - Order ",Ticket," closed at price ",ClosePrice);
               bool mod=OrderModify(Ticket, OrderOpenPrice(), OrderOpenPrice(),0,Red);
               if(mod){
                  Print("Trade modified successfully");
                  break;
               }
               else Print("Error trade not modified");
               
            }
            else Print("ERROR - CLOSE - error closing order ",Ticket," return error: ",GetLastError());
         }
      }
   }
   return;
}



bool TP1HitBuy;
bool TP1HitSell;

void CheckTP1Hit(){
   TP1HitBuy=false;
   TP1HitSell=false;

     double ATR = iATR(NULL,PERIOD_H4,14,1);
     
     double TP1PriceBuy = (OrderOpenPrice() + ATR);
     double TP1PriceSell = (OrderOpenPrice() - ATR);

   for( int i = 0 ; i < OrdersTotal() ; i++ ) {
      if( OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) == false ) {
         Print("ERROR - Unable to select the order - ",GetLastError());
         break;
      }
      if( OrderSymbol()==Symbol() && OrderLots()==LotSize)
      {
      if(OrderType()==OP_BUY && (iHigh(NULL,Timeframe,0) == TP1PriceBuy)) TP1HitBuy=true;
      if(OrderType()==OP_SELL && (iLow(NULL,Timeframe,0) == TP1PriceSell)) TP1HitSell=true;
      }
}
}



void OnTick()
  {
//---


   //Calling initialization, checks and technical analysis
   Initialize();
   CheckCanOrder();
   CheckMACross();
   CheckTP1Hit();

   if(TP1HitBuy)
   {
       CloseHalf(OP_BUY);
   }
   
   if(TP1HitSell)
   {
       CloseHalf(OP_SELL);
   }
}


The CloseHalf function should be perfectly fine as it was just a slightly modified version of a working CloseAll function. But the void CheckTP1Hit function and the if statements in the OnTick function are obviously wrong, I just don't know where to go from here. (Theres a bunch of other code, but this is all thats related to the TP1 feature)

I replaced the previous code with my updated code. This code doesn't return any errors, but when backtesting the stop loss is not modified. It may have to do with the way I'm using the ATR value to define the TP1 value. So the TP1 Price may not be what I'm intending it to be...

Documentation on MQL5: Constants, Enumerations and Structures / Indicator Constants / Price Constants
Documentation on MQL5: Constants, Enumerations and Structures / Indicator Constants / Price Constants
  • www.mql5.com
Calculations of technical indicators require price values and/or values of volumes, on which calculations will be performed. There are 7 predefined identifiers from the ENUM_APPLIED_PRICE enumeration, used to specify the desired price base for calculations. If a technical indicator uses for calculations price data, type of which is set by...
 

You are trying to modify an order after you have closed it.

When you partially close an order a new order is opened with a new ticket number and the remaining lots.

At this stage of your coding you a may find it easier to modify the SL first and then close the partial order.


I know that it is not obvious, but topics concerning MT4 and MQL4 have their own section.

In future please post in the correct section.

I will move your topic to the MQL4 and Metatrader 4 section.


 
mitch9104:


void CloseHalf(int Command){
   double ClosePrice=0;
   for( int i = OrdersTotal()-1 ; i >= 0  ; i-- ) {
      if( OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) == false ) {
         printf ("ERROR %d - Unable to select the order %d- ",GetLastError(),i);
         continue;
      }
      if( OrderSymbol()==Symbol() && OrderType()==Command && OrderLots()==LotSize) {
         if(Command==OP_BUY) ClosePrice=Bid;
         if(Command==OP_SELL) ClosePrice=Ask;
         double Lots=OrderLots();
         int Ticket=OrderTicket();
         for(int j=1; j<OrderOpRetry; j++){
            bool res=OrderClose(Ticket,Lots/2,ClosePrice,Slip,Red);
            if(res){
               Print("TRADE - CLOSE - Order ",Ticket," closed at price ",ClosePrice);
               bool mod=OrderModify(Ticket, OrderOpenPrice(), OrderOpenPrice(),0,Red);
               if(mod){
                  printf("Trade %d modified successfully", Ticket);
                  continue;
               }
               else Print("Error trade not modified");
               
            }
            else Print("ERROR - CLOSE - error closing order ",Ticket," return error: ",GetLastError());
	    
	    Sleep( 250 );
         }
      }
   }
   return;
}

When closing trades the approach is starting from the oldest in the pool.

Instead of terminating the FOR loop, is better to continue until the complete pool is evaluated.

Before re-trying again better to wait some time.

Add detailed info to the error messages

 
Fernando Morales:

When closing trades the approach is starting from the oldest in the pool.

Instead of terminating the FOR loop, is better to continue until the complete pool is evaluated.

Before re-trying again better to wait some time.

Add detailed info to the error messages

  1. The secondary loop must be broken out of instead of continued in case of success
  2. Also , if the trade is closed and not modified the loop will try to close the trade again 
  3. And lastly , when the half close occurs the ticket that is modified is the closed portion

Also , nice idea the colorcoding of issues ,im stealing it :) 

Now , the following 

#property strict
#include <LorioTools\CloseTools.mqh>

void in_your_trade_managing_function_when_you_realize_your_order_hit_tp1()
{
//an example of how to,given you detected that ticket "tp1ticket" is the order that hit tp1 - you will still build the detection yourself 
//and you will enjoy it
//so our assumed ticket 
int tp1ticket=1;//assumed value dont ever do this 
/*
you know which ticket it is so you also know the type ,the 100% lots ,the symbol ,the open price
otherwise you wasted a loop into your trades
*/
//we call the closer - again ,your editor will scream with errors            50%
close_request closer=CloseAnOrder(tp1ticket_type,tp1ticket,tp1symbol,tp1magic,50,tp1fulllots,10,500,100,clrAquamarine,false);
/*
notes : 
requires refresh : know if its needed in your broker
all tp1ticket_ are variables you should have collected upon detection
of your trade hitting tp1
attempts and delay b2in attempts ,have those as inputs in your ea int attempts uint delay
*/
//now check the closer result 
  if(closer.closed)//if it closed
  {
  //if a new ticket was found 
    if(closer.new_ticket!=-1){
    //do your mods here ....
    }
  }

}


Files:
CloseTools.mqh  13 kb
 
  1. Fernando Morales: When closing trades the approach is starting from the oldest in the pool.

    Changing that may not work for OP because of FIFO rules (he's in the USA).

    In the presence of multiple orders (one EA multiple charts, multiple EAs, manual trading,) while you are waiting for the current operation (closing, deleting, modifying) to complete, any number of other operations on other orders could have concurrently happened and changed the position indexing:

    1. For non-FIFO (non-US brokers), (or the EA only opens one order per symbol,) you can simply count down, in a position loop, and you won't miss orders. Get in the habit of always counting down.
                Loops and Closing or Deleting Orders - MQL4 programming forum
    2. For In First Out (FIFO rules — US brokers,) and you (potentially) process multiple orders per symbol, you must find the earliest order (count up,) close it, and on a successful operation, reprocess all positions.
                CloseOrders by FIFO Rules - Strategy Tester - MQL4 programming forum - Page 2 #16
                MetaTrader 5 platform beta build 2155: MQL5 scope, global Strategy Tester and built-in Virtual Hosting updates - Best Expert Advisors - General - MQL5 programming forum #1 № 11 ACCOUNT_FIFO_CLOSE

    3. and check OrderSelect in case earlier positions were deleted.
                What are Function return values ? How do I use them ? - MQL4 programming forum
                Common Errors in MQL4 Programs and How to Avoid Them - MQL4 Articles
    4. and if you (potentially) process multiple orders, must call RefreshRates() after server calls if you want to use, on the next order / server call, the Predefined Variables (Bid/Ask.) Or instead, be direction independent and just use OrderClosePrice().
  2. As Keith said in #1, don't try modifying after closing. You need a way to determine if you have alread done the partial close. Modify to BE and then close. If an order is already at BE or better, the close as already been done.

  3.  bool res=OrderClose(Ticket,Lots/2,ClosePrice,Slip,Red);
    1. You can't just use OrderLots()/2 because that is not a multiple of LotStep, and you can't close or have remaining less than MinLot.

    2. You also must check if you have already done it, to avoid repeated closing. Alternatives:

      • Move SL to Break Even+1 before the partial close. That way you know that you already did it.

      • Set a flag in persistent storage (files, global variables w/flush)

      • Open two orders initially, and close one (manually or by TP.)

 
void CloseHalf(int Command){
   double ClosePrice=0;
   for( int i = 0 ; i < OrdersTotal() ; i++ ) {
      if( OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) == false ) {
         Print("ERROR - Unable to select the order - ",GetLastError());
         break;
      }
      if( OrderSymbol()==Symbol() && OrderType()==Command && OrderLots()==LotSize) {
         if(Command==OP_BUY) ClosePrice=Bid;
         if(Command==OP_SELL) ClosePrice=Ask;
         double Lots=OrderLots();
         int Ticket=OrderTicket();
         for(int j=1; j<OrderOpRetry; j++){
            bool mod=OrderModify(Ticket, OrderOpenPrice(), OrderOpenPrice(),0,Red);
            if(mod){
                Print("Order ",Ticket," modified successfully");
                bool res=OrderClose(Ticket,LotSize/2,ClosePrice,Slip,Red);
                if(res){
                   Print("TRADE - CLOSE - Order ",Ticket," closed at price ",ClosePrice);
                   break;
               }
               else Print("ERROR - CLOSE - error closing order ",Ticket," return error: ",GetLastError());               
            }
            else Print("ERROR - Order ",Ticket," not modified");
            
         }
      }
   }
   return;
}


So I changed the order of the Close and Modify functions so now it modifies the stop loss to break even and then closes half the order. I replaced OrderLots() with LotSize which is an external variable, since TP1 always closes half of the original position size.

I ran a backtest on the first code I posted and it at least closed half of the position sometimes (even though the Sl wasn't changed), so I think that shows that the void CheckTP1Hit and OnTick functions are working.

But I just ran this new code and it didn't modify the stop loss or close any half positions at all...


Thanks for the CloseTools Lorentzos, I'll have to take some time to understand the functions but it looks like a handy way to find the ticket number of partially closed orders


(PS thanks for moving the post Keith, I didn't look hard enough and thought there was just a cluster of MQL4 and MQL5 posts, hence why I put MQL4 in the title)

Reason: