Blog
MQL5 Trade Retcodes: Read CTrade Results and Handle Failures
Quick Answer
In MQL5, checking whether a CTrade method returned true is not enough to confirm that a trade was executed. Inspect trade.ResultRetcode() and trade.ResultRetcodeDescription() after the request, and log relevant request details. A method result, a local terminal error from GetLastError(), and the trade server's retcode describe different stages of processing.
Key Facts
| Signal | What it tells you |
|---|---|
| CTrade method result | Whether the method's basic request checks succeeded |
| ResultRetcode() | The trade server/terminal result code for the latest request |
| ResultRetcodeDescription() | Human-readable description for logging |
| GetLastError() | A separate terminal/runtime error channel |
| Retcode context | Must be interpreted for the operation and execution mode |
Treat the result as part of the strategy's control flow. If an EA assumes every requested buy or modification succeeded, it may believe it has protection or exposure that does not exist. Log the symbol, side, volume, requested price, stops, and result so failures can be reproduced.
Classify a CTrade Result
#include <Trade/Trade.mqh>
CTrade trade;
enum TradeOutcome
{
TRADE_FAILED,
TRADE_PENDING,
TRADE_PARTIAL,
TRADE_COMPLETED
};
TradeOutcome ClassifyTradeResult(const bool methodResult,
const uint retcode)
{
if (!methodResult)
return TRADE_FAILED;
if (retcode == TRADE_RETCODE_DONE)
return TRADE_COMPLETED;
if (retcode == TRADE_RETCODE_DONE_PARTIAL)
return TRADE_PARTIAL;
if (retcode == TRADE_RETCODE_PLACED)
return TRADE_PENDING;
return TRADE_FAILED;
}
TradeOutcome OpenBuy(const double volume)
{
ResetLastError();
const bool methodResult = trade.Buy(volume, _Symbol);
const uint retcode = trade.ResultRetcode();
const int terminalError = GetLastError();
Print("Buy request: method=", methodResult,
", retcode=", retcode,
", description=", trade.ResultRetcodeDescription(),
", terminalError=", terminalError);
return ClassifyTradeResult(methodResult, retcode);
}
The returned states are deliberately not a Boolean “success.” TRADE_COMPLETED means the server reports this request completed; TRADE_PARTIAL means only part of it completed; TRADE_PENDING means an order was placed but is not confirmed filled; and TRADE_FAILED needs error handling. CTrade::Buy() returning true only says its basic request checks succeeded. Do not count TRADE_RETCODE_DONE_PARTIAL or TRADE_RETCODE_PLACED as a completed market buy. Inspect the actual position, deal, or order state and define what the EA should do next.
The exact allowed outcome depends on the operation and execution mode. For example, a pending-order placement naturally expects an order to be placed; a market entry should not label that same state as a completed fill. Consult the MQL5 trade server return-code reference and the documentation for the method you call.
Common Retcodes to Recognize
Some frequently encountered outcomes include:
TRADE_RETCODE_DONE: the requested operation completed.TRADE_RETCODE_DONE_PARTIAL: only part of the requested volume was completed.TRADE_RETCODE_PLACED: an order was placed; execution may still be pending.TRADE_RETCODE_REQUOTEorTRADE_RETCODE_PRICE_CHANGED: the requested price is no longer available or changed.TRADE_RETCODE_INVALID_STOPS: stop or take-profit values are invalid for the current symbol/market conditions.TRADE_RETCODE_NO_MONEY: available margin is insufficient.TRADE_RETCODE_MARKET_CLOSED: trading is not currently available.TRADE_RETCODE_TRADE_DISABLED: trading is disabled for the account, symbol, or terminal.TRADE_RETCODE_TOO_MANY_REQUESTS: the server is rejecting excessive requests.
Do not respond to every failure by retrying immediately. Retrying a stale price, invalid stop, disabled market, or insufficient-margin request without changing the relevant condition can create a request loop. For temporary conditions, use bounded retries with refreshed prices and appropriate delay; for configuration and permission failures, stop and report the issue.
Treat partial completion as its own state. If only part of a requested volume is filled, re-read current positions and pending orders before deciding what remains to be done. Blindly resending the original volume can over-enter. Likewise, a request accepted as placed may later be filled, rejected, or cancelled; monitor the order lifecycle rather than considering the first response the end of the workflow.
Use a small classification policy: completed, pending, partial, retryable, and terminal/configuration failure. The retryable class should be narrow, bounded, and idempotent where possible. Keep the original request context with every retry so you can diagnose a repeated rejection without producing noisy, unbounded logs.
Retcode vs. GetLastError()
GetLastError() reports a terminal/runtime error, such as a local API or data problem. ResultRetcode() reports the outcome associated with the trade request. They are not interchangeable. Capture each immediately in the relevant context, and do not infer the server response from a nonzero terminal error alone.
A robust log should include a timestamp, EA identifier, symbol, magic number, request type, requested volume, price and stops, method result, retcode, retcode description, and terminal error. Avoid logging credentials or sensitive account data.
For asynchronous trading, a request response and the later transaction event are separate parts of the lifecycle. Make sure the EA's state machine can handle a delayed response and does not mark a position open until it has observed the relevant order/deal/position state. Test this behavior with the execution mode and broker conditions you intend to use.
For volume issues, inspect the EA's risk-based lot calculation; for symbol ownership, review filtering positions by magic number.
Frequently Asked Questions
Does CTrade.Buy() returning true mean a position opened?
Not necessarily. Inspect the retcode and the resulting order/position state. A request may be placed for later execution or only partially filled.
Should my EA retry every failed trade?
No. Retry only when the failure is plausibly temporary and your strategy defines safe, bounded retry behavior. Refresh prices and re-check volume, stops, margin, and market status as appropriate.
Is GetLastError() the trade server retcode?
No. It is a separate terminal/runtime error channel. Use ResultRetcode() to inspect the trade request result.
Log, Classify, and Respond
Trade errors become actionable when the EA records request context and responds according to the failure class. Test rejection, partial-fill, and market-closed paths in a controlled environment. CodeFlowOS can produce an MQL5 draft with an optional platform check; read the verification notes. Trade handling still needs review for your broker and account.
What to do for each outcome
| State | Safe next step | |---|---| | Completed | Re-select the position or inspect the deal and record the actual fill volume and price. | | Partial | Re-read positions, deals, and remaining order volume before deciding whether to send another request. | | Pending | Track the order until it is filled, cancelled, or rejected; do not submit the same entry again just because the first response was not a fill. | | Failed | Capture the retcode, classify whether a changed request or later retry can help, and stop on permanent errors. |
For partial completion, the amount still unfilled is not automatically safe to resend. A later tick may have changed the price, the account exposure may have changed, or the order may still be active. Reconcile state first. Use a bounded retry policy and store enough request context to avoid duplicate entries after a terminal or EA restart.
When testing, force or simulate several different outcomes rather than testing only the happy path. Check whether the EA blocks duplicate requests while a placed order is pending, whether it detects a partial fill, and whether its logs distinguish a local terminal error from a server retcode. Also test the relevant account mode because netting and hedging change how resulting exposure is represented.
Related guides cover position ownership by magic number, risk-based lot sizing, daily loss controls, and indicator signal conversion. If the entry rule comes from TradingView, first compare the Pine-to-MQL5 execution models.
Try the converter, see the step-by-step conversion guide and plans, and treat compilation as a syntax check. Review trade-state handling and test it in a controlled account.