How to avoid over-optimization (curve fitting) when tuning an EA

Published 2026-09-14 · Ranar Algo

Over-optimisation is the simplest way to build a strategy that looks brilliant in the Strategy Tester and fails the moment it meets live money. The optimiser does not know what a market is — it only knows how to minimise a number you gave it. Ask it to maximise profit on one year of one symbol and it will find the noise, every time. Below is a checklist for keeping it honest.

Count parameters against trades

A rough rule that has served me well:

Hold back data, always

Split the history before you start, not after you see the result.

Test the neighbourhood, not the peak

The single best indicator of curve fitting is a spiky optimisation surface.

Change the conditions

An edge should survive a bit of reality:

A 359-trade GBPUSD H1 run that ends at −$3,953 with a 46% win rate is not a winning EA — but it is an honest one, tested over a sample large enough to be believed: [see a real backtest](https://mql.ranartech.com/r/EBjGC6HYYxvE). That is the sort of result you want from your own testing before you start tuning. If your optimised EA cannot survive being looked at this plainly, the parameters are doing the work, not the logic.

Give the optimiser a criterion that punishes complexity

Do not let the tester rank candidates on raw profit. It rewards luck, drawdown and parameter count equally. MQL5 lets you define your own criterion in OnTester():

//--- custom optimisation criterion: return per unit of drawdown,
//--- penalised for complexity and for thin samples
double OnTester()
{
   double profit = TesterStatistics(STAT_PROFIT);
   double trades = TesterStatistics(STAT_TRADES);
   double ddrel  = TesterStatistics(STAT_EQUITY_DDREL_PERCENT);

   // Reject anything we cannot judge: thin samples and zero drawdown
   if(trades < 100.0 || ddrel <= 0.0)
      return(0.0);

   // Count your own tunable inputs and keep this honest
   int params = 4;

   // Each parameter beyond the first costs ~3% of the score
   double complexity = MathPow(0.97, (double)MathMax(0, params - 1));

   // Reward return per unit of drawdown, not raw profit
   double score = (profit / ddrel) * complexity;

   return(score);
}

This will not find your edge for you. It simply stops the optimiser from handing you a 12-parameter monster with 40 trades and a beautiful equity curve. Note that OnTester() is only meaningful in the tester, and params is a number you maintain by hand — no API will count them for you.

Red flags checklist

The honest test

Before you go live, write down what you expect: trades per month, average win, worst drawdown, maximum losing streak. Then run the EA on a demo account and compare. If the live distribution looks nothing like the backtest, no amount of re-optimisation will fix it — the original fit was the problem. Fewer parameters, more trades, held-back data, and a score that rewards consistency over peak profit will keep you out of most of the trouble.

Build & backtest your own EA free →

Ranar Algo turns a plain-English strategy into a compiled MQL5 Expert Advisor with a real MetaTrader 5 backtest. You keep your IP. Past performance does not guarantee future results.