Three Factors That Determine Whether Your EA Needs Fixing or Rebuilding

Three Factors That Determine Whether Your EA Needs Fixing or Rebuilding

15 May 2026, 10:12
Boris Armenteros
0
30
Whether a struggling EA deserves targeted optimization or a full rebuild comes down to three structural factors: code structure quality, strategy logic validity, and state management architecture. I evaluate this regularly for clients, and the answer is never obvious from the outside.

The two expensive defaults

Traders almost always default to one extreme — "just fix this one thing" or "start from scratch." Both are wrong when applied to the wrong codebase.

I had a client who paid for three separate rounds of patching. First round: the trailing stop was not checking `MarketInfo(Symbol(), MODE_STOPLEVEL)` before calling `OrderModify()`, so the broker rejected modifications silently. Second round: `OrderSend()` calls had no return value checks — failed orders were invisible. Third round: the EA had no persistent state, so every terminal restart reset its position tracking.

Each patch was technically correct, but every fix uncovered the next structural problem. The total cost exceeded a rebuild.

The opposite mistake: a client wanted a full rebuild of an EA that had been running live for a year. The core logic was sound. The only problems were a trailing stop that did not account for spread widening and an order management function that occasionally sent duplicate close commands. Targeted fixes would have cost a fraction and shipped in days.

Factor 1: Code structure

Can the existing codebase absorb changes without cascading failures?

Fixable code has separated concerns — entry logic does not depend on risk management, which does not depend on order execution. You can replace one module without touching the others.

Unfixable code typically looks like this:

void OnTick()
{
   // 450 lines of nested conditions
   // Entry logic, position sizing, trailing stops,
   // order management, and logging all in one function
   
   if(iRSI(NULL, 0, 14, PRICE_CLOSE, 0) < 30)  // Why 14? Why 30?
   {
      if(AccountFreeMargin() > 100)
      {
         int ticket = OrderSend(Symbol(), OP_BUY, 0.1,  // Hardcoded lot
                                Ask, 3, 0, 0, "", 0, 0, clrNone);
         // No return value check — failed orders are invisible
      }
   }
}

Compare that with:

void OnTick()
{
   if(!IsNewBar()) return;
   
   int signal = EvaluateEntry();
   if(signal == SIGNAL_NONE) return;
   
   double lots = CalculateLotSize(IN_RiskPercent, GetStopLossDistance(signal));
   if(lots <= 0) return;
   
   ExecuteEntry(signal, lots);
   ManageOpenPositions();
}
The first version: every fix is a full regression test. The second: you can swap `ManageOpenPositions()` without touching entry logic.

Factor 2: Strategy validity

Code quality is irrelevant if the trading logic is curve-fitted.

I reviewed an EA with clean code — proper error handling, separated functions, reasonable naming. The entry logic was a moving average crossover with periods optimized across an 18-month EURUSD window. The Strategy Tester showed a strong equity curve for that period. On out-of-sample data — the 12 months before and after — consistent losses.

No amount of code restructuring fixes a flawed entry signal. Validate the strategy first with walk-forward testing, then decide if the code is worth keeping.

An EA built on a valid strategy with bad code is a rescue candidate. The hard part — finding an edge — is done. An EA built on a flawed strategy with clean code is still worthless.

Factor 3: State management

This is the factor that most often tips the decision from "fix" to "rebuild."

State management is architectural — woven into every function that reads or writes position data. It cannot be bolted on after the fact.

A concrete example: an EA tracking a basket of correlated positions using a global array. Clean code, valid strategy. On every terminal restart, the array reinitialized empty. The EA reopened positions on top of existing ones. After three restarts: triple the intended exposure.

The fix — adding file-based persistence — meant modifying every function that touched the basket array:

int OnInit()
{
   // Reconstruct basket state from existing orders
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
      if(OrderMagicNumber() != MAGIC_NUMBER) continue;
      
      // Rebuild internal tracking from live order data
      AddToBasketFromOrder(OrderTicket(), OrderLots(), OrderOpenPrice());
   }
   
   printf("%s: Reconstructed %d basket positions from live orders",
          __FUNCTION__, BasketCount());
   return INIT_SUCCEEDED;
}
By the time every function was touched, the "fix" and the "rebuild" converged to the same amount of work.

The decision matrix

Code Structure
Strategy
State Mgmt
Recommendation
Adequate 
Valid 
Adequate 
Fix — targeted optimization
Adequate 
Valid
Inadequate 
Partial rebuild — state rewrite
Inadequate 
Valid 
Adequate 
Partial rebuild — restructure code
Inadequate 
Valid 
Inadequate
Full rebuild
Any 
Unclear 
Any 
Pause — validate strategy first
Most common scenario in rescue projects: inadequate code, valid strategy, no state management. The strategy is worth saving. The code is not. Knowing this upfront prevents three rounds of patching that lead to the same conclusion.

Before committing money either way, ask the three questions. A code review takes hours, not weeks, and the information it produces prevents the expensive default.