Identify remaining order of an OrderCloseBy request

 

Dear all,

there is a problem for me to identify the remaining order of an OrderCloseBy request where the both orders have different lot sizes.

Let me explain on the outtake of the logfile

16:51:16 '2088713279': order buy market 3.00 DE30 sl: 0.0 tp: 0.0
16:51:16 '2088713279': request was accepted by server
16:51:16 '2088713279': request in process
16:51:16 '2088713279': order was opened : #22599398 buy 3.00 DE30 at 9661.3 sl: 0.0 tp: 0.0
16:51:46 '2088713279': order sell market 1.00 DE30 sl: 0.0 tp: 0.0
16:51:46 '2088713279': request was accepted by server
16:51:46 '2088713279': request in process
16:51:46 '2088713279': order was opened : #22599399 sell 1.00 DE30 at 9660.5 sl: 0.0 tp: 0.0
16:52:04 Expert CloseBy F40,H1: loaded successfully
16:52:07 '2088713279': close order #22599398 buy 3.00 DE30 at 9661.3 sl: 0.0 tp: 0.0 by order #22599399 sell 1.00 DE30 at 9660.5 sl: 0.0 tp: 0.0
16:52:07 '2088713279': request was accepted by server
16:52:07 '2088713279': request in process
16:52:07 '2088713279': order #22599398 was closed by #22599399
16:52:07 '2088713279': remainder of order #22599398 was opened : #22599400 buy 2.00 DE30 at 9661.3 sl: 0.0 tp: 0.0

First a 3 lots buy order was opened, (#22599398)
and afterwards a 1 lot sell order.   (#22599399)

In the next step an OrderCloseBy(22599398, 22599399) is submitted.

And now is happening some magic.

Order #22599398 (origin 3 lots) is closed and has a new lot size of 1.
Order #22599399 (origin 1 lots) is closed and has a new lot size of 0.

AND

there is a new order (#22599400, 2 lots buy) as the remainder of the original #22599398.

But this new automatically created order cannot be identified as the remainder of #22599398 and therefore i am not able to track this order.
This order is out of control of my trading system now!

I tried already to scan the logfile, but it will not be flushed to disk timely.

Any idea how to identify such a relation of closed and remaining (autogenerated) orders?

I think, i am not the only one having such a problem.

Best regards
Dirk


 
sahl04: Any idea how to identify such a relation of closed and remaining (autogenerated) orders?
Have you tried Magic#?
 

a lot of discussion in here

for example

 
All my brokers modify the order comment with the "from" word and the original order number.
 
Ovo: All my brokers modify the order comment with the "from" word and the original order number.
And other brokers do not. Don't depend.
sahl04: Any idea how to identify such a relation of closed and remaining (autogenerated) orders?
Do an OrderSelect loop by position and find the only existing order in the pool. The others can only be found in the history pool.
 
OrderCloseTime() of the 2 closeby orders should be the same as OrderOpenTime() of the new order ... I know that would work in tester the problem is how to be sure it would work correctly in live trading
 
SDC:
OrderCloseTime() of the 2 closeby orders should be the same as OrderOpenTime() of the new order ... I know that would work in tester the problem is how to be sure it would work correctly in live trading

not realy if OrderCloseTime() = 20:01:15.999 & OrderOpenTime() is gonna be even a split second later ? that means 20:01:16
 

Well yes that could happen depending on how the server is coded. If I was coding the server side of the OrderCloseBy() function, I would call the time once and assign that timestamp to the closed tickets and the open time of the replacement new ticket, after all the whole thing probably happens in a nano second, but we have no way of knowing what they did.

 

Thanks for all suggestions!

SDC:
OrderCloseTime() of the 2 closeby orders should be the same as OrderOpenTime() of the new order ... I know that would work in tester the problem is how to be sure it would work correctly in live trading

This might be not correct.

I did some testing and found out the following

First approach with OrderCloseBy(22610340, 22610342) - the smaller lots are closing the larger lots (Original lot sizes were #22610340 with 3 lots and #22610342 with 1 lot)

OrderTicket OrderSymbol OrderType OrderLots OrderOpenTime OrderOpenPrice OrderCloseTime OrderClosePrice OrderComment OrderMagicNumber
22610340 DE30 0 1 1392720003 9652.3 1392720022 9651.8 partial close 12345
22610342 DE30 1 0 1392720013 96518 1392720022 9651.8 close hedge by #22610340 12345
22610344 DE30 0 2 1392720003 9652.3 1392720027 9651.5 partial close 12345


Second approach with OrderCloseBy(22610545, 22610544) - the larger lots are closing the smaller lots (Original lot sizes were #22610544 with 3 lots and #22610545 with 1 lot)

OrderTicket OrderSymbol OrderType OrderLots OrderOpenTime OrderOpenPrice OrderCloseTime OrderClosePrice OrderComment OrderMagicNumber
22610544 DE30 0 0 1392723882 9641 1392723900 9641 close hedge by #22610545 12345
22610545 DE30 1 1 1392723892 9640.5 1392723900 9641 partial close 12345
22610546 DE30 0 2 1392723882 9641 1392723906 9640.3 from #22610545 12345


The matching remainder order were #22610344 on the first approach and #22610546 on the second approach.


The rules to find the matching remainder should be:

The order which is the source for the remaining order is the one with the larger lot size.

The OrderSymbol must be the same (obvious)

The OrderType must be the same (the remainder cannot be an OP_SELL when the origin was an OP_BUY and vice versa)

The order lots of the remaining order must be the difference of the origin lot sizes ( 3 - 1 = 2 in both cases)

The OrderOpenTime and OrderOpenPrice must be the same.

The OrderComment does not help to find the match.

The OrderMagicNumber must be the same (if used).


Depending on these rules here is my approach


int OrderCloseByReturnRemainder(int ticketToClose, int ticketClosing, color arrowColor = CLR_NONE)
{
   int result = 0;
   int lastError = 0;
   int selectedValueGroup = 0;
   double remainderLotsExpected = 0;
   
   string symbols[2];
   int orderTypes[2];
   double lots[2];
   datetime openTimes[2];
   double openPrices[2];
   int magicNumbers[2];
   
   //-------- Section 1 - Take values of the order to be closed
   
   if(OrderSelect(ticketToClose, SELECT_BY_TICKET, MODE_TRADES))
   {
      symbols[0] = OrderSymbol();
      orderTypes[0] = OrderType();
      lots[0] = OrderLots();
      openTimes[0] = OrderOpenTime();
      openPrices[0] = OrderOpenPrice();
      magicNumbers[0] = OrderMagicNumber();
   }
   else
   {
      lastError = GetLastError();
   }

   //-------- Section 2 - Take values of the closing order
   
   if(lastError == 0)   
   {
      if(OrderSelect(ticketClosing, SELECT_BY_TICKET, MODE_TRADES))
      {
         symbols[1] = OrderSymbol();
         orderTypes[1] = OrderType();
         lots[1] = OrderLots();
         openTimes[1] = OrderOpenTime();
         openPrices[1] = OrderOpenPrice();
         magicNumbers[1] = OrderMagicNumber();
      }
      else
      {
         lastError = GetLastError();
      }
   }

   //-------- Section 3 - Verify symbol and magic number
   
   if(symbols[0] != symbols[1])
   {
      lastError = -5000; // Symbols does not match
   }
   else if(magicNumbers[0] != magicNumbers[1])
   {
      lastError = -5001; // Magic numbers does not match
   }

   //-------- Section 4 - Depending on the lot sizes decide which values are expected for the remainder order
   
   if(lastError == 0)
   {
      remainderLotsExpected = MathAbs(lots[0] - lots[1]);
      
      if(lots[0] >= lots[1])
      {
         // The source of the remainder order is the order with the 'ticketToClose' ticket number
         selectedValueGroup = 0;
      }
      else
      {
         // The source of the remainder order is the order with the 'ticketClosing' ticket number
         selectedValueGroup = 1;
      }
   }

   //-------- Section 5 - Perform the OrderCloseBy operation
   
   if(lastError == 0)
   {
      if(!OrderCloseBy(ticketToClose, ticketClosing, arrowColor))
      {
         lastError = GetLastError();
      }
   }

   //-------- Section 6 - Identify the remainder order
   
   if(lastError == 0)
   {
      if(remainderLotsExpected != 0)
      {
         for(int i = 0; i < OrdersTotal(); i++)
         {
            if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
            {
               if(OrderSymbol()      == symbols[selectedValueGroup] &&
                  OrderType()        == orderTypes[selectedValueGroup] &&
                  OrderLots()        == remainderLotsExpected &&
                  OrderOpenTime()    == openTimes[selectedValueGroup] &&
                  OrderOpenPrice()   == openPrices[selectedValueGroup] &&
                  OrderMagicNumber() == magicNumbers[selectedValueGroup])
               {
                  // Bingo - we got it.
                  result = OrderTicket();
                  break;
               }
            }
         }
         
         if(result == 0)
         {
            // We are expecting a remainder order but couldn't find it!
            // Already closed in the meantime? Unlikely, buth who knows.
            // Or too strange rules for identification.
            lastError = -5002;
         }
      }
      else
      {
         // There are no remainderLotsExpected - then there will be no remainder order
         result = 0;
      }
   }
   
   if(lastError != 0)
   {
      result = lastError;
   }
   
   return(result);
}


You can test it with following sample.

Uncomment OrderCloseBy... commands to try diferent versions.

You can cut and paste the logfile output to an excel sheet.


int init()
  {
//----
// !!!!!!!!!!!!! EXECUTE JUST ON DEMO ACCOUNT TO PREVENT LOSS !!!!!!!!!!!!!!!!!!!!
   int i = 0;
   int remainderTicket = 0;
   int magicNumber = 12345;
   
   int ticket1 = OrderSend(Symbol(), OP_BUY, 3, Ask, 10, 0, 0, "...", magicNumber, 0);
   
   Sleep(10000);
   
   int ticket2 = OrderSend(Symbol(), OP_SELL, 1, Bid, 10, 0, 0, "...", magicNumber, 0);
   
   Sleep(8000);
   
   //OrderCloseBy(ticket1, ticket2);
   //OrderCloseBy(ticket2, ticket1);

   //remainderTicket = OrderCloseByReturnRemainder(ticket1, ticket2);
   remainderTicket = OrderCloseByReturnRemainder(ticket2, ticket1);

   Sleep(5000);
   
   for(i = 0; i < OrdersTotal(); i++)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(Symbol() == OrderSymbol())
         {
            if(OrderMagicNumber() == magicNumber)
            {
               OrderClose(OrderTicket(), OrderLots(), Bid, 20);
            }
         }
      }
   }
   
   Print("OrderTicket\t",
         "OrderSymbol\t",
         "OrderType\t",
         "OrderLots\t",
         "OrderOpenTime\t",
         "OrderOpenPrice\t",
         "OrderCloseTime\t",
         "OrderClosePrice\t",
         "OrderComment\t",
         "OrderMagicNumber");
         
   for(i = 0; i < OrdersHistoryTotal(); i++)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
      {
         if(OrderTicket() >= ticket1 && Symbol() == OrderSymbol())
         {
            Print(OrderTicket(), "\t",
                  OrderSymbol(), "\t",
                  OrderType(), "\t",
                  OrderLots(), "\t",
                  OrderOpenTime(), "\t",
                  OrderOpenPrice(), "\t",
                  OrderCloseTime(), "\t",
                  OrderClosePrice(), "\t",
                  OrderComment(), "\t",
                  OrderMagicNumber());
         }
      }
   }
   
   if(remainderTicket == 0)
   {
      Print("There is no remaining order.");
   }
   else if(remainderTicket < 100000)
   {
      Print("OrderCloseByReturnRemainder returns error number ", remainderTicket);
   }
   else
   {
      Print("Remainder ticket number is #", remainderTicket);
   }
   
//----
   return(0);
  }


Best regards

Dirk

 

Note

After the function call, the contents of _LastError are not reset. To reset this variable, you need to call ResetLastError().

 
qjol:

Note

After the function call, the contents of _LastError are not reset. To reset this variable, you need to call ResetLastError().


My online help in MetaEditor is telling me something else!


int GetLastError( )

The function returns the last occurred error, then the value of special last_error variable where the last error code is stored will be zeroized. So, the next call for GetLastError() will return 0.


And executing of

   double p = Bid;
   OrderSend(Symbol(),OP_SELL,0.1,p,50,NormalizeDouble(p+1000*Point,Digits),0);
   
   Alert("First call to GetLastError() returns ", GetLastError());   
   Alert("Second call to GetLastError() returns ", GetLastError());  

against DE30 CFD is giving me this.


First call to GetLastError() returns 131

Seconds call to GetLastError() returns 0


Best regards

Dirk

Reason: