Przejdź do treści
MQL5 · MetaTrader 5 · po polsku
kod odczarowany

MQL5 / Pułapki

OnTester: własne kryterium optymalizacji

Zysk netto jako kryterium premiuje przebiegi z jedną szczęśliwą transakcją. Jak napisać miarę, która odrzuca przypadek.

1 fragment kodu z tej strony przeszedł przez kompilator MetaEditor build 6090, 2026-08-05.

Większość twórców EA używa testera strategii w trybie optymalizacji, wybierając z rozwijanej listy kryterium "Custom Max" (Maksimum niestandardowe) i... zapomina, że to nie jest magia. Wybór tej opcji bez zaimplementowania funkcji OnTester() sprawia, że tester optymalizuje po prostu zysk netto (Profit), co zazwyczaj prowadzi do przeuczenia (overfittingu) na historycznych danych i katastrofy na koncie rzeczywistym.

Dlaczego zysk netto to zły pomysł?

Optymalizacja pod kątem maksymalnego zysku faworyzuje strategie, które ryzykowały bardzo dużo, miały ogromne zaciągnięcia (Drawdown) lub opierały się na 2-3 "szczęśliwych" zleceniach. Prawdziwy, profesjonalny algorytm handlowy musi być oceniany przez pryzmat stosunku zysku do ryzyka, płynności i stabilności. W MQL5 możesz zdefiniować własne, matematyczne kryterium oceny (Fitness Function), które tester genetyczny będzie próbował zmaksymalizować.

OnTester: własne kryterium optymalizacjiZysk netto jako kryterium premiuje przebiegi z jedną szczęśliwą transakcją. Jak napisać miarę, która odrzuca przypadek. Wpływ Custom Fitness na algorytm genetyczny MT5 Cel: Maksymalny Zysk Algorytm znajduje ścieżkę: Wielka pozycja -> Szczęśliwy strzał Wynik: DD = 60%, Profit = 2000$ Przeuczenie (Overfitting) Cel: OnTester() (Zysk / MaxDD) Algorytm szuka kompromisu: Małe pozycje -> Stabilny wzrost Wynik: DD = 10%, Profit = 800$ Stabilność (Robustness)

Implementacja zaawansowanego kryterium

Stwórzmy kryterium łączące zysk netto, maksymalne zaciągnięcie (w procentach) oraz współczynnik Sharpe'a, aby karać strategie o dużej zmienności wyników. Zwracana wartość musi być typu double. Im wyższa, tym "lepszy" osobnik w oczach algorytmu genetycznego.

// Ta funkcja jest wywoływana po zakończeniu każdego pojedynczego przebiegu (pass)
// w optymalizatorze, jeśli wybrano "Custom max".
double OnTester() {
    // Pobieranie statystyk z testera
    double netProfit = TesterStatistics(STAT_PROFIT);
    double profitFactor = TesterStatistics(STAT_PROFIT_FACTOR);
    double maxDrawdown = TesterStatistics(STAT_BALANCE_DDREL_PERCENT); // DD w %
    double sharpeRatio = TesterStatistics(STAT_SHARPE_RATIO);
    int totalTrades = (int)TesterStatistics(STAT_TRADES);
    
    // Kary i filtry logiki
    // 1. Odrzucamy strategie, które w ogóle nie handlowały lub mają ujemny zysk
    if(totalTrades < 10 || netProfit <= 0) return -1000.0;
    
    // 2. Kara za zbyt duże zaciągnięcie. Jeśli DD > 20%, drastycznie obcinamy wynik.
    double ddPenalty = 1.0;
    if(maxDrawdown > 20.0) {
        ddPenalty = 20.0 / maxDrawdown; // Np. dla 40% DD, kara wynosi 0.5
    }
    
    // 3. Wymagamy minimalnej stabilności (Sharpe > 0.5)
    if(sharpeRatio < 0.5) return -500.0;
    
    // Wzór fitness: Zysk * Współczynnik Zysku * Kara za DD * Współczynnik Sharpe'a
    // To sprawia, że algorytm woli 1000$ zysku z DD 5% i PF 2.0,
    // niż 5000$ zysku z DD 50% i PF 1.1.
    double customFitness = netProfit * profitFactor * ddPenalty * sharpeRatio;
    
    return customFitness;
}