Every EA project starts with a brief. The brief describes entry conditions, exit logic, position sizing, and timeframe. It is usually thorough on paper. In practice, it is almost never complete.
The observation that repeats across hundreds of projects: EAs fail in live trading not because of code quality, but because of incomplete requirements. The brief covered the normal case. The code was built for the normal case. The live market did something outside the normal case — and the EA had no handling rule because nobody had written one.
The work we do in the first two days of a project addresses this gap before it becomes expensive.
Requirements Elicitation: The Questions That Define the Edge Cases
The first session is not a coding session. It is a structured question-and-answer that surfaces conditional decisions the brief doesn't cover.
Three questions from our standard requirements checklist that expose the most expensive gaps:
What happens if two signals fire on the same bar? This scenario typically doesn't appear in strategy descriptions because traders reason in terms of normal market flow. In a recent project, the client said "that can't happen." We pulled 3 years of backtest data and found 67 occurrences. Resolution: first signal wins, second is discarded, with a log entry recording the collision. Without that session, the EA would have applied an arbitrary default — whichever branch the code encountered first.
What should happen if the terminal disconnects during an order modification? A network interruption during a stop-loss modification can leave a position in a stuck state — the modification is neither confirmed nor rolled back. This requires a recovery routine in `OnInit()` that checks for discrepancy between stored state and current position state:
int OnInit() { // Check for in-flight modification states from previous session for(int i = OrdersTotal() - 1; i >= 0; i--) { if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue; if(OrderMagicNumber() != MAGIC_NUMBER) continue; double storedSL = ReadStoredSL(OrderTicket()); // From persistent state if(storedSL > 0 && MathAbs(OrderStopLoss() - storedSL) > Point * 5) { printf("%s: Stuck modification detected | Ticket=%d | CurrentSL=%.5f | StoredSL=%.5f", __FUNCTION__, OrderTicket(), OrderStopLoss(), storedSL); SetModificationPending(OrderTicket(), storedSL); // Re-queue for next tick } } return INIT_SUCCEEDED; }
Without this recovery check, the position stays stuck until manual intervention. The brief had described the strategy. It had not described what recovery from a disconnected modification looked like — because the client had not considered the scenario.
At what equity level does risk reduction activate, and does it apply to new positions or existing ones? On three projects this year, different stakeholders gave different answers to this question. The EA was built on one interpretation. When the question was asked properly, it revealed that the intended behavior was a third answer that neither group had articulated.
Each of these questions resolves into a code-ready decision before a function is written.
Broker Constraint Mapping: 5 Parameters Before the Risk Module
After the strategy brief, we validate broker constraints. These five parameters affect the EA's behavior regardless of what the strategy spec says:
- Minimum lot size (`SYMBOL_VOLUME_MIN`) — the floor on position sizing. A strategy with 0.01 granularity cannot execute on a broker where the instrument minimum is 0.10.
- Maximum lot size (`SYMBOL_VOLUME_MAX`) — the ceiling, relevant for grid and recovery strategies.
- Stop level (`SYMBOL_TRADE_STOPS_LEVEL`) — the minimum distance in points between the current price and a stop-loss or take-profit. On ECN brokers, this is frequently reported as zero in documentation but is non-zero in practice. Stop placement logic that passes every backtest silently rejects orders in live conditions when this is not accounted for.
- Spread behavior during news — affects time filter logic and pending order placement windows.
- Margin calculation mode — determines how equity and free margin are calculated when positions are open, which affects grid and compound-position sizing.
One recent project required 0.01 lot granularity. The broker's minimum on the specified instrument was 0.10. That mismatch was caught by requesting the symbol specification sheet in session one — before the risk module was built. Discovering it after the module was complete would have required rebuilding the position sizing logic from scratch.
The Edge-Case Map
The artifact produced by this phase is an internal document: a structured list of every identified edge condition, the agreed handling decision, and the conditional branch it maps to. It is not a deliverable to the client. It functions as the conditional logic specification for the developer.
When we audit EAs built by other developers — or generated by AI code tools — the absence of this document is almost universal. The code handles the normal case correctly. It fails on first contact with anything outside it. That is not a code quality problem. It is a requirements problem.
The Numbers
Two comparable projects from the same quarter, same complexity tier:
| With upfront discovery | Without | |
|---|---|---|
| Revision rounds | 1 | 3 |
| Extra hours billed | 0 | 34 |
| Delivery | On time | 3 weeks late |
| Post-delivery support requests | 0 | 4 |
The code quality was the same. The specification quality was not.
Twelve to sixteen hours of upfront analysis is not overhead. It is the work that makes the code work in production — because production will encounter every scenario the brief didn't mention.


