About two months ago I got a panicked message from a trader I used to work with. His EA was opening trades just fine, but for some reason, it wasn't closing them. The stop loss and take profit levels were being set, or so he thought, but when price hit those levels, nothing happened. Trades just kept running until he manually intervened.
I asked him to send me the code. What I found wasn't a complex bug. It was a combination of two things: a misunderstanding of how MQL4 evaluates if-conditions, and a dangerous assumption about the OrderSend and OrderModify functions running without delays. Neither of these is particularly well-documented in the standard tutorials you see online.
The Zero-and-One Trap Nobody Talks About
The EA was checking a function's return value before deciding to open or close orders. The code looked something like this:
``
if (CheckOpenConditions()) {
OrderSend(Symbol(), OP_BUY, Lots, Ask, 3, 0, 0, "Buy", Magic, 0, clrGreen);
}
int CheckOpenConditions() {
// some logic here
return(0); // returns 0 when conditions are NOT met
}
`
The problem was staring right at me. In MQL4, the if-condition evaluates numeric values . A return of 0 means FALSE, and any non-zero value means TRUE. But the developer who wrote this had inverted the logic. CheckOpenConditions() was returning 0 to indicate "conditions not met" – which technically works – but later in the same EA, there was another function returning -1 for "error" and 0 for "success". That's where things fell apart.
`
int CloseOrder(int ticket) {
if (OrderClose(ticket, Lots, Bid, 3, clrRed)) {
return(0); // success!
} else {
return(-1); // failed!
}
}
`
And the calling code was written as:
`
if (CloseOrder(ticket)) {
Print("Order closed successfully");
} else {
Print("Order close failed");
}
`
This is a classic trap. The MQL4 documentation doesn't explicitly warn about this in bold letters, but the underlying principle is simple: if(-1) evaluates to TRUE because -1 is not zero . So when CloseOrder() returned -1 to indicate failure, the calling code treated it as success. The trader had been staring at the chart wondering why his EA wasn't closing orders, and the whole time the EA thought it was closing them just fine.
The OrderModify Delay That Sank Everything
The other problem was more subtle. The EA was opening orders without stop loss or take profit, then immediately using OrderModify() to add them. The code looked textbook-perfect at first glance:
`
int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 3, 0, 0, "MyOrder", 12345, 0, clrGreen);
if (ticket > 0) {
OrderSelect(ticket, SELECT_BY_TICKET);
double stopLoss = Ask - 50 _Point;
double takeProfit = Ask + 100 _Point;
OrderModify(ticket, OrderOpenPrice(), stopLoss, takeProfit, 0, clrNONE);
}
`
I've seen this exact pattern copied and pasted across countless forums and even some paid EAs. The problem? There's a tiny time gap between OrderSend() returning and OrderModify() executing. In that fraction of a second, the price can move. If the calculated stop loss or take profit becomes invalid (e.g., a Buy order's stop loss ends up above the current market price), OrderModify() fails silently. The EA doesn't check the return value, so it carries on as if everything worked .
The Fix I Applied
The solution was actually simpler than the problem. For the if-condition trap, I rewrote all return-value logic to be explicit:
`
bool CheckOpenConditions() {
// return true when conditions met, false otherwise
if (someLogic) return(true);
return(false);
}
bool CloseOrder(int ticket) {
if (OrderClose(ticket, Lots, Bid, 3, clrRed)) {
return(true);
} else {
Print("Order close failed. Error: ", GetLastError());
return(false);
}
}
`
Now the calling code works as expected: if (CloseOrder(ticket)) only fires when CloseOrder() explicitly returns true.
For the OrderModify issue, the fix was even cleaner. Instead of two separate calls, I bundled everything into a single OrderSend():
`
double stopLoss = NormalizeDouble(Ask - 50 _Point, _Digits);
double takeProfit = NormalizeDouble(Ask + 100 _Point, _Digits);
int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 3, stopLoss, takeProfit, "SafeOrder", 12345, 0, clrGreen);
if (ticket < 0) {
Print("Order failed. Error: ", GetLastError());
}
`
This approach is more reliable because it eliminates the timing gap entirely. The stop loss and take profit are submitted with the original order request, so there's no separate modification step that can fail . The MQL4 documentation on trading functions doesn't explicitly recommend one approach over the other, but in practice, the single-call method is far more robust for live trading environments.
iCustom Signals: When EAs Talk to Indicators
One more thing I noticed from that debugging session. The EA was using iCustom() to pull signals from a custom indicator. But the parameters passed didn't match the indicator's external variable order. The MQL4 Book explains that iCustom() parameters must correspond with the declaration order of external variables in the custom indicator . This is easy to miss when you're modifying an EA quickly.
I had to go back and count the parameters in the indicator's source to make sure the order was correct. There's no compiler error for this – the EA just returns garbage values and your trading logic falls apart. Cross-checking parameter order is tedious but essential. I now keep a checklist for every EA that uses iCustom() calls.
Lessons Learned
The trader's EA worked perfectly after these fixes. No changes to the trading strategy itself – just cleaning up the logic and the order execution pattern. What I took away from this is that MQL4's simplicity can be deceptive. The language doesn't hold your hand with type safety or explicit Boolean coercion warnings. It assumes you know that -1 is truthy and that OrderModify()` can fail without telling you.References: MQL4 Book – Combined Use of Programs (book.mql4.com), MQL4 Documentation – Trading Functions (docs.mql4.com), MQL4 Tutorial – If-else Logic Traps (book.mql4.com).
This article is originally published on FXEAR.com, original content, reproduction without authorization is prohibited.