Summary: Advanced guide to MQL5 OnTesterInit and ParameterSetRange functions. Learn to eliminate invalid parameter combinations before optimization, dynamically restructure search space, and prevent genetic algorithm waste.




When optimizing EAs with the MetaTrader 5 genetic optimizer, one critical inefficiency is rarely discussed: the optimizer wastes generations testing invalid parameter combinations. Every time the EA rejects parameters in OnInit (e.g., Fast MA period > Slow MA period), that pass becomes dead genetic material, reducing population diversity and optimization quality .

1. The Hidden Cost Of Parameter Rejection

Standard practice for handling invalid combinations is simple rejection:

```cpp
int OnInit() {
if(FastMAPeriod >= SlowMAPeriod)
return INIT_PARAMETERS_INCORRECT; // Pass wasted
return INIT_SUCCEEDED;
}
```

This creates a cascade of problems for genetic algorithms :
  • Each rejected pass reduces effective population size

  • The optimizer cannot "learn" which regions are invalid

  • Early generations may lose promising genes due to adjacent invalid combos

  • Optimization quality degrades proportionally to rejection rate


  • 2. The Solution: OnTesterInit + ParameterSetRange

    MQL5 provides two powerful functions for dynamic parameter control during optimization setup :

    ```cpp
    // Get current parameter ranges
    bool ParameterGetRange(string name, bool &enable,
    long &value, long &start, long &step, long &stop);
    bool ParameterGetRange(string name, bool &enable,
    double &value, double &start, double &step, double &stop);

    // Set new parameter ranges dynamically
    bool ParameterSetRange(string name, bool enable,
    long value, long start, long step, long stop);
    bool ParameterSetRange(string name, bool enable,
    double value, double start, double step, double stop);
    ```

    Critical limitation: `ParameterSetRange` can ONLY be called from `OnTesterInit()`, before optimization begins .

    3. Complete Dynamic Restructuring Implementation

    Problem: Two parameters `FastMA` and `SlowMA` where `FastMA` must always be less than `SlowMA`. Traditional optimization would waste ~50% of passes.

    Solution: Create a shadow parameter that represents valid combinations only:

    ```cpp
    // Input parameters visible to optimizer
    input int FastMA = 5; // Will be disabled in optimization
    input int SlowMA = 20; // Will be disabled in optimization

    // Shadow parameter - the optimizer sees only this
    sinput int ValidComboIndex = 0; // Index into valid combinations

    struct SValidPair {
    int fast;
    int slow;
    };

    // Pre-calculate all valid combinations
    SValidPair GetValidPair(int index, int fastStart, int fastStop, int fastStep,
    int slowStart, int slowStop, int slowStep) {
    int count = 0;
    for(int f = fastStart; f <= fastStop; f += fastStep) {
    for(int s = slowStart; s <= slowStop; s += slowStep) {
    if(f < s) {
    if(count == index) {
    SValidPair result = {f, s};
    return result;
    }
    count++;
    }
    }
    }
    SValidPair empty = {-1, -1};
    return empty;
    }

    int GetTotalValidCount(int fastStart, int fastStop, int fastStep,
    int slowStart, int slowStop, int slowStep) {
    int count = 0;
    for(int f = fastStart; f <= fastStop; f += fastStep) {
    for(int s = slowStart; s <= slowStop; s += slowStep) {
    if(f < s) count++;
    }
    }
    return count;
    }

    void OnTesterInit() {
    // Read original parameter ranges from optimizer settings
    bool fastEnabled, slowEnabled;
    long fastValue, fastStart, fastStep, fastStop;
    long slowValue, slowStart, slowStep, slowStop;

    if(!ParameterGetRange("FastMA", fastEnabled, fastValue, fastStart, fastStep, fastStop) ||
    !ParameterGetRange("SlowMA", slowEnabled, slowValue, slowStart, slowStep, slowStop)) {
    Print("Failed to get parameter ranges");
    return;
    }

    // Calculate total valid combinations count
    int totalValid = GetTotalValidCount((int)fastStart, (int)fastStop, (int)fastStep,
    (int)slowStart, (int)slowStop, (int)slowStep);

    // Disable original parameters from optimization
    ParameterSetRange("FastMA", false, fastValue, fastStart, fastStep, fastStop);
    ParameterSetRange("SlowMA", false, slowValue, slowStart, slowStep, slowStop);

    // Enable shadow parameter with range 0..totalValid-1
    ParameterSetRange("ValidComboIndex", true, 0, 0, 1, totalValid - 1);

    PrintFormat("Dynamic restructuring: %d valid combinations, original space would be %d passes",
    totalValid, ((fastStop - fastStart)/fastStep + 1) * ((slowStop - slowStart)/slowStep + 1));
    }
    ```

    4. Reconstructing Parameters Inside OnInit

    During actual backtest passes, reconstruct the real parameters from the index:

    ```cpp
    // Global variables to store reconstructed values
    int g_fastMA = 5;
    int g_slowMA = 20;

    int OnInit() {
    // Retrieve the shadow parameter value
    int comboIndex = ValidComboIndex;

    // Need to know original ranges - either hardcode or pass via additional shadow params
    int fastStart = 3, fastStop = 30, fastStep = 1;
    int slowStart = 5, slowStop = 100, slowStep = 1;

    SValidPair pair = GetValidPair(comboIndex, fastStart, fastStop, fastStep,
    slowStart, slowStop, slowStep);

    if(pair.fast == -1) {
    Print("Invalid combo index: ", comboIndex);
    return INIT_PARAMETERS_INCORRECT;
    }

    g_fastMA = pair.fast;
    g_slowMA = pair.slow;

    return INIT_SUCCEEDED;
    }
    ```

    5. Passing Original Ranges Via Shadow Parameters

    A more robust approach: store original range information in additional shadow parameters :

    ```cpp
    // Add these to the input section
    sinput ulong FastShadow = 0; // Packed: start|(stop<<16)|(step<<24)
    sinput ulong SlowShadow = 0; // Packed: start|(stop<<16)|(step<<24)

    void OnTesterInit() {
    // ... previous code ...

    // Pack range information into 64-bit integers
    ulong fastPacked = (ulong)fastStart | ((ulong)fastStop << 16) | ((ulong)fastStep << 32);
    ulong slowPacked = (ulong)slowStart | ((ulong)slowStop << 16) | ((ulong)slowStep << 32);

    // Set shadow parameters (disabled, fixed values)
    ParameterSetRange("FastShadow", false, fastPacked, fastPacked, 1, fastPacked);
    ParameterSetRange("SlowShadow", false, slowPacked, slowPacked, 1, slowPacked);

    // ... rest of code ...
    }

    // In OnInit, unpack ranges
    void UnpackRange(ulong packed, int &start, int &stop, int &step) {
    start = (int)(packed & 0xFFFF);
    stop = (int)((packed >> 16) & 0xFFFF);
    step = (int)((packed >> 32) & 0xFFFF);
    }
    ```

    6. Performance Impact Quantification

    | Optimization Method | Passes Tested | Valid Passes | GA Efficiency |
    |---------------------|---------------|--------------|----------------|
    | Standard (with rejection) | 1000 | ~500 | 50% |
    | Dynamic Restructuring | 500 | 500 | 100% |

    By eliminating invalid combinations entirely, the genetic algorithm explores only viable parameter space, achieving equivalent or better results in half the time .

    7. Extension To Multi-Parameter Constraints

    The same technique handles complex constraints:

    ```cpp
    // Example: Three parameters where A + B < C
    int GetValidCount(int aStart, int aStop, int aStep,
    int bStart, int bStop, int bStep,
    int cStart, int cStop, int cStep) {
    int count = 0;
    for(int a = aStart; a <= aStop; a += aStep)
    for(int b = bStart; b <= bStop; b += bStep)
    for(int c = cStart; c <= cStop; c += cStep)
    if(a + b < c) count++;
    return count;
    }
    ```

    Reference: MQL5 Documentation, "ParameterGetRange and ParameterSetRange" (mql5.com/docs); MQL5 Programming Guide, Chapter 6 "Tester Automation" .