Examples: Error 146 ("Trade context busy") and How to Deal with It

 

New article Error 146 ("Trade context busy") and How to Deal with It has been published:

The article deals with conflict-free trading of several experts on one МТ 4 Client Terminal. It will be useful for those who have basic command of working with the terminal and programming in MQL 4.

Author: Andrey Khatimlianskii

 
There is one line in the TradeIsNotBusy function from this article which looks suspicious, can someone please verify whether it is correct? (or agree with me that it is not correct)

void TradeIsNotBusy()
  {
    int _GetLastError;
    // at testing, there is no sense to divide the trade context - just terminate 
    // the function
    if(IsTesting()) 
      { 
        return(0); 
      }
    while(true)
      {
        // if the expert was terminated by the user, прекращаем работу
        if(IsStopped()) 
          { 
            Print("The expert was terminated by the user!"); 
            return(-1); 
          }
       ...

I am talking about
if(IsStopped())... return (-1);
What if the expert is stopped in the middle of executing the order? Then it will enter this function, hit the "if stopped" statement and return -1 without releasing the global semaphore! Other experts won't be able to trade because this expert left the GlobalVariable equal to 1.
Would it not be better to remove this if-statement to ensure that the global variable is always released?

Also - I think it is a *very good idea* to call the TradeIsNotBusy function from the deinit function. Tis way we'll ensure we always release the trade context before termination.
 

I believe that there is a problem in this code. 


It is in the first while() loop of TradeIsBusy().  We have:

while(true)

{

    ...

    if ( GlobalVariableCheck( "TradeIsBusy" ) ) break;  //Checks to see if the variable exists, and if it does, then break out of this loop
    else { ... }

     ...
     if ( GlobalVariableSet("TradeIsBusy", 1.0 ) > 0 ) return(1); //Create the variable and set it to "Red Light", return to calling thread that we own the trade context

    else { ... }

}

The problem (I believe) is that if the variable did not exist and two threads called this code simultaneously, then we could end up with both threads thinking that they own the trade context.  Suppose thread1 checked for the variable and found that it didn't exist so thread1 continues inside the loop.  Meanwhile, before it has the chance to create the global variable, another thread has entered into the function--- thread2.  Thread2 also finds that the variable doesn't exist and so it also continues.   Now they both call GlobalVariableSet() and although one of them actually does create the variable, they are both told that the setting of the variable to the value of 1.0 is a success and so they both return 1.  Both threads now believe that they own the trade context.  I know that the chances of this happening are really very very low, however, the chance does still exist.


Of course, this observation is based upon my understanding that each EA executes inside of its own thread, but that the trade context only has one thread available (which is why we need the semaphore).


Please someone correct me if I'm wrong.

 

I don't think I have added the template right lol

I have an error '.' - unexpected token what d I have to do with these ... what do I put in there

Any help would be great

 
4x4ever:
What if the expert is stopped in the middle of executing the order? Then it will enter this function, hit the "if stopped" statement and return -1 without releasing the global semaphore! Other experts won't be able to trade because this expert left the GlobalVariable equal to 1.
Would it not be better to remove this if-statement to ensure that the global variable is always released?

Read the article:

In the use of functions TradeIsBusy() and TradeIsNotBusy(), only one problem can occur: If the expert is removed from the chart after the trade context has become busy, the variable TradeIsBusy will remain equal to 1. Other experts will not be able to trade after that.

The problem can be solved easily: The expert should not be removed from the chart when it is trading ;)

 
bwilhiteforex:

The problem (I believe) is that if the variable did not exist and two threads called this code simultaneously, then we could end up with both threads thinking that they own the trade context.

You can create Global Variable before the start of experts ;)

Otherwise you have a little chance to see this problem.

 

What about order closure?

Can an "OrderClose" function call fail (return false) due to busy trade thread?

If so, shall "GetLastError" return error code 146 (ERR_TRADE_CONTEXT_BUSY)?

 
Simha:

What about order closure?

Can an "OrderClose" function call fail (return false) due to busy trade thread?

If so, shall "GetLastError" return error code 146 (ERR_TRADE_CONTEXT_BUSY)?

Yes, you should use TradeIsBusy() function before perform any trade operation (open, close, modify, etc.).
 

Hello,

this is my first post of a question here. I have read the article, created the relevant functions and placed them in the include folder.

I have several EA's running, so when I discovered your article, it was perfect timing for my query. This EA is one of several EAs running at the same time.

Please refer to my MT4 code outline from the EA that I have compiled.

Thank you, in advance for any help.

My questions are these:

1. if (TradeIsBusy() < 0) return (-1); where is the best place to put this line? position AAA, BBB, or CCC?
2. where would the RefreshRates() actually be most appropriately placed?
3. TradeIsNotBusy(); Have I correctly place that line, or would it be better place inside ManageTrade()?

start()
   {
        // .. check status of existing trades...
	AAA
	ManageTrade()
	//
   } // end of the start() routine 

        void ManageTrade()
        {
               ....
                if (maketrade==true)
           {
              BBB .....
                bool result = SendSingleTrade(tradetype, Comment, Lot, price, stop, take, expiry);
                //
           if (result)
           {
                .....
                Print(Symbol() + " Managetrade() has sent a " + tradetype + " trade");
           }
           if (!result)
           {
                .....
                int err=GetLastError();
                ......
        } // end of ManageTrade()
        //


        bool SendSingleTrade()
        {
           .....
           ......
	   CCC
           ticket = OrderSend(Symbol(),type, lotsize, price, slippage, 0, 0, comment, MagicNumber, orderexpiry, col);
           .....
           if (!result)
           {
                int err=GetLastError();
                // I will assume that error 146 will be thrown at this line.
           }
   
           //Got this far, so order send succeeded
           //
           TradeIsNotBusy();  // this seems the most appropriate place, but perhaps?? it would be better placed up one level in ManageTrade()?
           //
           return(true);
           //
        } // end bool SendSingleTrade()
 

spgandau:

My questions are these:

1. if (TradeIsBusy() < 0) return (-1); where is the best place to put this line? position AAA, BBB, or CCC?
2. where would the RefreshRates() actually be most appropriately placed?
3. TradeIsNotBusy(); Have I correctly place that line, or would it be better place inside ManageTrade()?

bool SendSingleTrade()
{
    // wait until the trade context is free and then occupy it (if an error occurs, leave it)
    if(TradeIsBusy() < 0) return( false ); 

    // refresh the market info
    RefreshRates();

    // calculate the StopLoss and TakeProfit levels, and the lot size
    ...
    // open a position
    ticket = OrderSend(Symbol(),type, lotsize, price, slippage, 0, 0, comment, MagicNumber, orderexpiry, col);
    // set the trade context free
    TradeIsNotBusy();
    if( ticket < 0 ) 
    { 
        Alert("Error opening position # ", GetLastError()); 
    }
    return(0);
}
I hope I've answered all your questions ;)
 
Yes that's wrong. You must release the lock before calling return(-1)
Reason: