Summary: A technical guide to EA parameter optimization covering combinatorial explosion math, genetic algorithm mechanics (selection, crossover, mutation), overfitting avoidance strategies, and practical MQL4 implementation tips for robust strategy development.




# EA Parameter Optimization: A Practical Guide to Genetic Algorithms and Overfitting Avoidance

The Combinatorial Explosion Problem



When optimizing an EA with multiple parameters, brute-force testing becomes mathematically impossible. Consider a typical scenario: 20 binary switches and 20 input variables with 10 possible values each. The total combination count equals 20²⁰ ≈ 1.048 × 10²⁶ possibilities .

To put this number in perspective:
  • It exceeds the number of sand grains on Earth (~7.5 × 10¹⁸)

  • It surpasses the number of stars in the Milky Way (~10¹¹)

  • Testing one combination per second would take longer than the age of the universe


  • This is why brute-force enumeration fails. The solution is genetic algorithm (GA) optimization, which MT4 and MT5 already implement natively.

    How Genetic Algorithm Optimization Works



    GA simulates biological evolution to find near-optimal parameter sets without exhaustive search . The process follows five steps:

    1. Initialization (Population Generation)



    Instead of testing all combinations, GA randomly generates hundreds or thousands of parameter sets. Each set represents an "individual" in the population.

    2. Fitness Evaluation (Performance Testing)



    Each parameter set runs through your backtest data. The terminal calculates a fitness score based on your chosen criterion:
  • Balance: Highest final balance

  • Profit Factor: Gross profit / gross loss

  • Expected Payoff: Average trade profitability

  • Maximal Drawdown: Lowest drawdown (lower is better)

  • Custom: User-defined OnTester() return value


  • 3. Selection (Survival of the Fittest)



    Higher-fitness individuals have greater probability of being selected for reproduction. Lower-fitness individuals get淘汰.

    4. Crossover (Reproduction)



    Selected individuals exchange "genetic material" (parameter values) to create offspring. For example, the stop-loss setting from Parent A combines with the take-profit setting from Parent B.

    5. Mutation (Random Variation)



    Random parameters flip to new values with low probability, introducing diversity and preventing premature convergence to local optima.

    This cycle repeats for dozens or hundreds of generations. Population average fitness improves over time, eventually converging on a high-performing parameter set .

    Practical Optimization Strategy: The Layered Approach



    Step 1: Fix What Shouldn't Be Optimized



    Before running GA, identify parameters that represent strategy logic rather than tunable values.

    For a grid trading EA:
  • Fix: Whether to use point-based or grid-based spacing (strategy choice)

  • Optimize: The specific spacing value (once strategy type is fixed)


  • This reduces your search space dramatically .

    Step 2: Set Realistic Optimization Ranges



    In MT4 Tester, configure your optimization boundaries :

    | Parameter | Start | Step | Stop |
    |-----------|-------|------|------|
    | TakeProfit | 20 | 10 | 100 |
    | StopLoss | 15 | 5 | 80 |
    | LotSize | 0.01 | 0.01 | 0.10 |

    Never include more than 5-7 parameters in a single optimization run.

    Step 3: Use Forward Testing for Validation



    GA finds parameters that excel on historical data. This may indicate overfitting, not robustness. The solution is out-of-sample validation:

    1. Split your data: 70% in-sample (optimization), 30% out-of-sample (validation)
    2. Run GA on in-sample data only
    3. Test the "champion" parameters on out-of-sample data
    4. If performance degrades significantly (>30% drop), your EA is overfit

    Detecting Future Functions in Custom Indicators



    Before converting any indicator into an EA for optimization, verify it contains no future functions — calculations that repaint historical signals based on current price .

    Two Detection Methods:



    Method 1 — Source Code Review (White Box)
    Scan for loops that modify historical array values. Any assignment to `shift` values other than 0 indicates repainting.

    Method 2 — Visual Observation (Black Box)
    Load the indicator on a 1-minute chart. Watch for historical signal changes as new candles form. If past buy/sell arrows disappear or relocate, the indicator has future functions.

    > Indicators with future functions produce excellent backtest results but fail in live trading. Never optimize an EA based on repainting indicators.

    Loading Custom Indicators via iCustom()



    Once you confirm an indicator has no future functions, integrate it into your EA using `iCustom()` :

    ```cpp
    // Function signature
    double iCustom(
    string symbol, // Symbol (NULL = current)
    int timeframe, // Period (0 = current chart)
    string name, // Indicator filename (without .ex4)
    ... , // Indicator parameters (if any)
    int mode, // Line index (0-based, from SetIndexBuffer)
    int shift // Shift (0 = current candle, 1 = previous)
    );
    ```

    Practical Example: RSI Divergence Indicator



    ```cpp
    // Assuming a custom divergence indicator:
    // Line 0 = bullish divergence price level
    // Line 1 = bearish divergence price level
    // (EMPTY_VALUE indicates no signal)

    double bullishDiv = iCustom(NULL, 0, "Divergence_Indicator", 0, 1);
    double bearishDiv = iCustom(NULL, 0, "Divergence_Indicator", 1, 1);

    if (bullishDiv != EMPTY_VALUE && OrdersTotal() == 0) {
    // Bullish divergence detected on previous candle
    OrderSend(Symbol(), OP_BUY, lotSize, Ask, 3, 0, 0, "Divergence Buy", magic);
    }
    ```

    Critical Consideration: Current vs. Previous Candle



    Using `shift=0` (current candle) causes signals to appear/disappear as price moves, leading to phantom signals during backtesting. For robust optimization, use `shift=1` (completed candle) .

    OrderSend() Fundamentals for EA Development



    The `OrderSend()` function executes trade operations :

    ```cpp
    int OrderSend(
    string symbol, // Currency pair (Symbol() for current)
    int cmd, // OP_BUY, OP_SELL, or pending orders
    double volume, // Lot size
    double price, // Open price (Ask for BUY, Bid for SELL)
    int slippage, // Maximum slippage in points (typically 2-3)
    double stoploss, // Stop loss price (0 if none)
    double takeprofit, // Take profit price (0 if none)
    string comment, // Order comment (e.g., "MyEA_v1")
    int magic, // Unique EA identifier
    datetime expiration, // Pending order expiry (0 for market orders)
    color arrow_color // Arrow color on chart
    );
    ```

    Simple Market Order Example



    ```cpp
    int start() {
    double lotSize = 0.1;
    int slippage = 3;
    int magicNumber = 20240601;

    // BUY order
    int ticket = OrderSend(
    Symbol(), // Current symbol
    OP_BUY, // Buy order
    lotSize, // 0.1 lots
    Ask, // Current ask price
    slippage, // 3 points slippage allowed
    Bid - 50 * Point, // Stop loss 50 points below Bid
    Bid + 100 * Point, // Take profit 100 points above Bid
    "GA_Optimized_EA", // Comment
    magicNumber, // Unique ID
    0, // No expiration
    clrGreen // Green arrow
    );

    if (ticket < 0) {
    Print("Order failed. Error: ", GetLastError());
    }
    return 0;
    }
    ```

    The OnTester() Custom Optimization Criterion



    For advanced optimization, implement `OnTester()` in your EA to define custom fitness metrics :

    ```cpp
    double OnTester() {
    // Retrieve backtest statistics
    double profitFactor = TesterStatistics(STAT_PROFIT_FACTOR);
    double maxDrawdown = TesterStatistics(STAT_EQUITY_DD_PERCENT);
    double sharpeRatio = TesterStatistics(STAT_SHARPE_RATIO);

    // Custom scoring: prioritize Sharpe ratio with drawdown penalty
    double fitness = sharpeRatio * 100;
    if (maxDrawdown > 20.0) fitness *= 0.5; // Penalty for excessive drawdown
    if (profitFactor < 1.3) fitness *= 0.8; // Penalty for low profit factor

    return fitness;
    }
    ```

    Optimization Pitfalls to Avoid



    | Pitfall | Consequence | Prevention |
    |---------|-------------|------------|
    | Overfitting | Great backtest, live failure | Use out-of-sample validation |
    | Curve-fitting | Performance collapses in new markets | Test across multiple market regimes |
    | Ignoring transaction costs | Real trading loses despite backtest profit | Include spread + commission in backtest |
    | Optimizing too many parameters | Spurious correlations found | Limit to 5-7 parameters per run |
    | Survivorship bias | Backtest on dead pairs only | Include delisted symbols in testing |

    Recommended Workflow for Robust EA Optimization



    1. Write clean, modular EA code with external input parameters
    2. Verify no future functions in any custom indicators used
    3. Set realistic parameter ranges (don't let GA hunt extremes)
    4. Run GA optimization with population size 500-1000, generations 50-100
    5. Select top 10 parameter sets from GA results
    6. Forward test each set on out-of-sample data (30% reserved)
    7. Choose the set with best consistency, not highest backtest profit
    8. Monte Carlo simulation to assess robustness under random trade order

    ---

    References:
    1. Eabang. "MT4MT5优化参数的遗传算法." August 2025.
    2. MetaTrader 4 Help. "Setup — Expert Optimization."
    3. FxGecko. "MT4编程:如何把指标应用到外汇EA上?" March 2023.
    4. MQL4 Book. "Opening and Placing Orders — OrderSend()."
    5. MQL4 Book. "Common Functions — Comment(), MessageBox(), PlaySound()."
    6. MQL4 Book. "Program Execution — EA Lifecycle."