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ć.
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;
}