If you’re migrating from MQL4 or debugging a place administration bug you can not reproduce in Technique Tester, learn this earlier than altering anything.
What the Documentation Truly Says
The MQL5 reference for `CTrade::PositionClose()` is technically correct. Each overloads are listed. The return worth is documented as a boolean. What the documentation doesn’t state plainly is what `true` truly means by way of account state: the request handed inside validation and was despatched to the dealer. Nothing extra.
That is distinct from the outdated MQL4 contract. `OrderClose()` in MQL4 was synchronous — when it returned `true`, the order was settled and the following line of code ran in opposition to a constant account state. Builders who constructed programs on that contract and are actually porting to MT5 carry the belief ahead, typically with out realizing it.
The Async Hole in Stay Situations
When `PositionClose()` returns `TRADE_RETCODE_DONE`, the shut request has been acknowledged. The dealer affirmation is pending. On ECN brokers beneath regular load, the time between `TRADE_RETCODE_DONE` and the place truly disappearing from `PositionSelect()` is roughly 300–800ms.
Technique Tester doesn’t replicate this hole. Each backtest will move cleanly. The failure surfaces at market open on the primary stay day.
The sensible consequence: if the following line of code after `PositionClose()` calls `PositionSelect()` to examine account state earlier than operating entry logic, it should discover the outdated place nonetheless current. If entry logic is condition-gated on “no open place,” it should both skip the entry or open a second place alongside the one being closed. The account briefly holds double publicity with no error within the log.
Right here is the naive sample that produces this:
bool closed = m_trade.PositionClose(Image()); if(closed) { if(!PositionSelect(Image())) { } }
The Deferred Verification Sample
The right sample defers any logic that is dependent upon a closed account state till place elimination is confirmed on the following tick:
bool m_closePending = false; bool ClosePosition(const string image) { if(!m_trade.PositionClose(image)) Image=%s", __FUNCTION__, m_trade.ResultRetcode(), image); return false; m_closePending = true; return true; } void OnTick() { if(m_closePending) { if(!PositionSelect(_Symbol)) { m_closePending = false; } return; } }
The flag blocks all dependent logic till `PositionSelect()` returns `false`, confirming precise elimination. No sleep, no arbitrary delays — only a state examine on the following tick.
OnTradeTransaction Timing
`OnTradeTransaction` fires as a part of the affirmation sequence, nevertheless it fires earlier than the place registry has totally up to date. Studying `PositionSelect()` inside `OnTradeTransaction` will return stale state within the hole interval. Use `OnTradeTransaction` to set flag state solely — by no means to learn place state.
void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &outcome) { if(trans.kind == TRADE_TRANSACTION_DEAL_ADD) { m_transactionReceived = true; } }
Dealing with 4 Return Codes Appropriately
Not all retcodes from `PositionClose()` are equal:
- `TRADE_RETCODE_DONE` — Request accepted. Apply deferred verification.
- `TRADE_RETCODE_REQUOTE` — Value modified throughout request. Retry instantly utilizing the present bid or ask.
- `TRADE_RETCODE_REJECTED` — Request was invalid. Examine image, margin necessities, and cease ranges earlier than retrying.
- `TRADE_RETCODE_TIMEOUT` — No affirmation obtained. That is the harmful case: the order might have stuffed and the affirmation was misplaced in transit. At all times confirm place state independently earlier than retrying. A blind retry on a `TIMEOUT` that stuffed will open a second place.
uint retcode = m_trade.ResultRetcode(); if(retcode == TRADE_RETCODE_DONE) { m_closePending = true; } else if(retcode == TRADE_RETCODE_REQUOTE) { m_retryClose = true; } else if(retcode == TRADE_RETCODE_REJECTED) Image=%s", __FUNCTION__, _Symbol); else if(retcode == TRADE_RETCODE_TIMEOUT) { if(!PositionSelect(_Symbol)) { m_closePending = false; } else { m_retryClose = true; } }
What This Seems Like in a Manufacturing EA
The total sample combines the entire above: `ClosePosition()` units the pending flag, `OnTick()` checks and clears it after confirming elimination, and retcode dealing with branches to the proper response for every case.
This sample isn’t advanced. The documentation describes the return contract precisely. The hole between documentation and manufacturing actuality is in how the migration from MQL4 carries a synchronous assumption into an asynchronous execution surroundings.
We see this sample inflicting account state bugs in roughly a 3rd of the MT5 EA rescue work we deal with every year. It passes each backtest. It fails at market open. The repair is fewer than 20 strains.


