The three passes we make over an Expert Advisor before we look at the strategy
A clean compile tells you the syntax is valid. That is the whole of its claim. It does not tell you that the correct order was selected before the close, that a rejected request was noticed, or that the Expert Advisor still knows what it holds after a terminal restart. Those are behavioural properties, and no compiler evaluates them.
We review a lot of code that other people wrote — some generated by an AI assistant, some bought cheaply, some inherited from a developer who has since gone quiet. The review always runs in the same order, and the strategy is the last thing we look at. Here is the sequence, in case it is useful to you on your own code.
Pass one: what does this EA know at startup?
The first question is where the EA's knowledge of the world comes from. If the only answer is "global variables set earlier in this session," the EA is one restart away from amnesia. It will initialise, find nothing in memory, conclude it holds no positions, and open another one.
The fix is not clever. State that matters must be rebuilt from the terminal, which is the only authoritative source:
Note the magic-number filter. Without it, an EA sharing a terminal with others will happily adopt a position it did not open. That is a second failure mode hiding inside the first, and it also compiles.
Pass two: what was selected before the operation?
The second pass follows every close, modify, and partial close backwards to the selection that precedes it. In MQL4 this is OrderSelect() ; in MQL5 it is PositionSelect() , PositionSelectByTicket() , or the ticket you pass into a CTrade call directly. The mechanism differs; the failure does not.
An operation performed on the wrong ticket is syntactically perfect. The compiler has no opinion about it. The Strategy Tester frequently has no opinion either, because in a single-position test there is only one ticket to get right. It surfaces in production, on the first account running more than one Expert Advisor, as a wrong lot size or an orphaned trade.
Read these pairs individually. There is no pattern-match that finds them, because the line that misbehaves is never the line that looks wrong.
Pass three: who reads the result?
Every trade request returns something. The third pass asks what the code does with it.
The first form is the one we find most often. It is not that the developer decided errors were unimportant — it is that nothing forced the question. A discarded return value means a requote, an invalid stop, or a rejected volume passes without a log line. The trader's first notification arrives days later in the balance, with no record of the cause.
Whether you use CTrade or raw OrderSend() , the principle holds: the retcode is the only thing that distinguishes a request that was sent from a request that worked.
The short version you can run today
Three searches, one afternoon:
- Every close or modify call — confirm a selection precedes it.
- Every trade request — confirm something reads the return value.
- Restart the terminal with a position open — confirm the EA still recognises it.
None of these requires a review, a tester run, or a second opinion. They will not tell you whether your strategy has an edge. They will tell you whether the code around it is capable of executing that strategy honestly, which is a different question and a prior one.
If all three come back clean, your infrastructure is in better shape than most of what we are sent. If they do not, you now know which pass to start with.


