MT5 introduces a highly dangerous trap for developers: OrderSend() returning true only means the server successfully verified your request structure, not that the trade was actually executed. You must query result.retcode to understand the actual outcome.
If you receive TRADE_RETCODE_TIMEOUT (10022), it means the trade server did not respond. The order may have perfectly executed, but the acknowledgment was lost. Retrying blindly here is exactly how double-fills happen.
Code: Select all
bool ExecuteSafeOrder_MT5(MqlTradeRequest &request, int magic) {
int maxRetries = 3;
int delayMs = 500;
request.magic = magic; // Embed the unique signal ID for idempotency
MqlTradeResult result;
for(int attempt = 1; attempt <= maxRetries; attempt++) {
// 1. Idempotency Check: Scan active positions on the broker server
for(int i = 0; i < PositionsTotal(); i++) {
ulong posTicket = PositionGetTicket(i);
if(posTicket > 0) {
if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetString(POSITION_SYMBOL) == request.symbol) {
PrintFormat("ABORT RETRY: Position %d verified open for Magic %d", posTicket, magic);
return true;
}
}
}
// 2. Re-sync market quotes before retrying to prevent off-quote rejections
MqlTick lastTick;
SymbolInfoTick(request.symbol, lastTick);
request.price = (request.type == ORDER_TYPE_BUY) ? lastTick.ask : lastTick.bid;
// 3. Execution Attempt
ResetLastError();
ZeroMemory(result);
bool sent = OrderSend(request, result);
// 4. Verify actual execution, not just structure acceptance
if(sent && (result.retcode == TRADE_RETCODE_DONE || result.retcode == TRADE_RETCODE_PLACED)) {
return true; // Actual execution success
}
// 5. Error Routing via server return codes
uint ret = result.retcode;
if(ret == TRADE_RETCODE_REQUOTE || ret == TRADE_RETCODE_CONNECTION || ret == TRADE_RETCODE_TIMEOUT || ret == TRADE_RETCODE_PRICE_CHANGED) {
PrintFormat("Transient Code %u - Attempt %d/%d. Retrying...", ret, attempt, maxRetries);
Sleep(delayMs + (MathRand() % 250)); // Exponential backoff with Jitter
delayMs *= 2;
} else {
PrintFormat("Terminal Code %u (System Err: %d) - Aborting.", ret, GetLastError());
break;
}
}
return false;
}