curious behaviour for two EA in parallel - page 2

 
7bit:

So this means you have actually had the very same error in your own EA and definitely found that it happens in an otherwise correct OrderSend() call and is definitely an MT4 bug? Can you intentionally reproduce the error? Or are you also only speculating?

FYI, I've also seen this. It mostly occurred when MT4 was under very heavy, artificial load, but I did see some occasional examples in real-life usage as well.

I used to be able to reproduce this reliably: I'd set some heavy-duty EAs going and, within an hour or so, I'd always get an incidence of this problem. But I haven't tried reproducing it in the 18 months since my post.
 
7bit:

So this means you have actually had the very same error in your own EA and definitely found that it happens in an otherwise correct OrderSend() call and is definitely an MT4 bug? Can you intentionally reproduce the error? Or are you also only speculating?


It's an OrderSend function that is called at the same time from two instances of one EA. A race condition in OrderSend function. This is not reproducible during back-test because I can't simulate execution of 2 (or more) instances of one EA running in 2 (or more) different markets (someone might try it using a demo account).

You may write this test: 1/ run two instances of one EA code each operating on different market, both trading against one server, 2/ both EA instances must attempt to call OrderSend at the same time (at the same second, see it in both logs above) - each instance wants to open, 3/ one instance opens buy stop, second one a sell stop order. => no matter whether you check trade context before OrderSend, you will come to situation when the first EA instance is returned a trade number of trade that was open by second instance - the opposite positions will result in the error message. Collierab's EA got trade number of EURUSD trade in EA instance operating on GBPUSD. I got EURUSD trade in USDJPY market The only chance how to detect irrelevant trade number is to compare Symbol() to the OrderSymbol() of trade number (unless you run two instances in 1 market).

 
Try using a semaphore to avoid the problem
//+------------------------------------------------------------------+
//| Trade context semaphore.                                         |
//+------------------------------------------------------------------+
void RelTradeContext(){         // Set global variable for GetTradeContext = 0.
/*bool  need2refresh;           // Export=T to GetTradeContext=R|T|F,
                                // setDIR=R|F, start=F, ModifyStops=R|F */
    need2refresh = true;        // Assume caller did a OrderSend/Modify/Close.
    while( !(IsTesting() || IsStopped()) ){
        GlobalVariableSet(TC.GV.Name, 0.0); // Unlock the trade context.
        int _GetLastError = GetLastError();
        if (_GetLastError == 0) break;
        Comment("RelTradeContext: GlobalVariableSet('",
                TC.GV.Name,"',0.0)-Error #", _GetLastError );   Sleep(1000);
    }
}   // void RelTradeContext()

int  Random(int min, int max){  return( MathRand()/32768.0*(max-min+1)+min );  }
#define ERR_146_MIN_WAIT_MSEC    1000   // Trade context busy random delay min
#define ERR_146_MAX_WAIT_MSEC    5000   // Trade context busy random delay max
void RandomSleep(){ Sleep(Random(ERR_146_MIN_WAIT_MSEC,ERR_146_MAX_WAIT_MSEC));}
#define ERR_146_TIMEOUT_MSEC    60000   // Trade context busy maximum delay
#include <stderror.mqh>                 // ERR_GLOBAL_VARIABLE_NOT_FOUND
int GetTradeContext(int maxWaitMsec = ERR_146_TIMEOUT_MSEC){
//  bool    need2refresh;               // Import from RelTradeContext
    /*Return codes:*/{
        #define TC_LOCKED        0  //  Success. The global variable TC.GV.Name
    //                                  was set to 1, locking out others.
        #define TC_REFRESH      +1  //  Success. The trade context was initially
    //                                  busy, but became free. The market info
    //                                  needed to be refreshed. Recompute trade
    //                                  values if necessary.
        #define TC_ERROR        -1  //  Error, interrupted by the user. The
    //                                  expert was removed from the chart, the
    //                                  terminal was closed, the chart period
    //                                  and/or symbol was changed, etc.)
        #define TC_TIMEOUT      -2  //  Error, the wait limit was exceeded.
    /*************************************************************************/}
    if ( IsTesting() ){                     // Only one EA runs under the tester
        if (need2refresh)   Refresh();
        return(TC_LOCKED);                  // Context always available.
    }
    /* If there is no global variable, create it. If the variable == 1 wait
     * until another thread sets it back to 0. Set it to 1. */
    int     startWaitingTime    = GetTickCount(),   // Remember start time.
            GTCreturn           = TC_LOCKED;        // Assume an idle context.
    bool    TC_locked           = false;            // Not yet.
    while(true){
        if ( IsStopped() ){     // The expert was terminated by the user, abort.
            Print("GTC: The expert was terminated by the user!");
            if (TC_locked)  RelTradeContext();
            return(TC_ERROR);
        }
        if (!TC_locked) {                           // Set the semaphore.
            if (GlobalVariableSetOnCondition( TC.GV.Name, 1.0, 0.0 )){
                TC_locked=true;                     // Semaphore now set.
                continue;                           // Check for non-sema EAs.
            }
            int _GetLastError   = GetLastError();   switch(_GetLastError){
            case 0:                             // Variable exists but is
                break;                          // already set. Need to wait.
            case ERR_GLOBAL_VARIABLE_NOT_FOUND:
                GlobalVariableSet(TC.GV.Name, 0.0); // Create it.
                _GetLastError   = GetLastError();
                if (_GetLastError == 0) continue;
                Print(  "GetTradeContext: GlobalVariableSet(", TC.GV.Name,
                            ", 0.0) Failed: ",  _GetLastError );    // Error
                return(TC_ERROR);
            default:
                Print(  "GetTradeContext:GlobalVariableSetOnCondition('",
                        TC.GV.Name, "',1.0,0.0)-Error #", _GetLastError);
                break;                      // Exit switch, wait and retry.
            }   // switch(_GetLastError)
        }   // Set the semaphore.
        else if (!IsTradeContextBusy()){            // Cleanup and return.
            if (GTCreturn == TC_REFRESH){           // Clear the Wait comment
                Comment(WindowExpertName(),": ", VERSION);
                need2refresh=true;
            }
            if (need2refresh)   Refresh();
            return(GTCreturn);
        }
        int delay = GetTickCount() - startWaitingTime ;
        if (delay > maxWaitMsec){                   // Abort.
            Print("Waiting time (", maxWaitMsec/1000, " sec) exceeded!");
            if (TC_locked)  RelTradeContext();
            return(TC_TIMEOUT);
        }
        Comment("Wait until another expert finishes trading... ", delay/1000.);
        RandomSleep();
        GTCreturn = TC_REFRESH;                     // Will need to refresh.
    }   // while
    /*NOTREACHED*/
}   // GetTradeContext

 
WHRoeder:
Try using a semaphore to avoid the problem

The code in https://www.mql5.com/en/articles/1412 does much the same general thing. It has the advantage of being fairly widely used. EAs which use the code in https://www.mql5.com/en/articles/1412 will all play nicely with each other. An EA which uses bespoke code for doing a similar thing will play nicely with multiple copies of itself, but not with other EAs. Therefore, there's a benefit in using the semi-standard code even if you think that you have a better version. (The crucial point is obviously using the same global variable which is used by https://www.mql5.com/en/articles/1412.)

Reason: