OnTester

Fonksiyon, test işleminden sonra gerekli eylemleri gerçekleştirmek için Tester olayı meydana geldiğinde uzman danışmanlarda çağrılır.

double  OnTester(void);

Geri dönüş değeri

Test sonuçlarını değerlendirmek için özel kriter optimizasyonunun değeri  

Not

OnTester() fonksiyonu, yalnızca uzman danışmanları test ederken kullanılabilir ve esasen girdi parametrelerini optimize ederken 'Custom max' kriteri olarak kullanılan bir değerin hesaplanması için tasarlanmıştır.

Genetik optimizasyon sırasında, bir nesil içerisindeki sonuçları sıralama azalan düzende gerçekleştirilir. Bu, en yüksek değere sahip sonuçların optimizasyon kriteri açısından en iyisi olduğu anlamına gelir. Bu tür sıralama için en kötü değerler sonda yerleşir ve devamında atılır. Dolayısıyla, gelecek neslin oluşturulmasında yer almazlar.

Böylece, OnTester() fonksiyonu, yalnızca kendi test sonuç raporlarınızı oluşturmanıza ve kaydetmenize olanak tanımaz, ayrıca alım-satım stratejisinin en iyi parametrelerini bulmak için optimizasyon sürecini kontrol etmenizi sağlar.

Aşağıda, özel kriter optimizasyonunun hesaplanması için bir örnek verilmiştir. Düşünce, denge grafiğinin doğrusal regresyonunu hesaplamaktır. Denge grafiği kullanılarak bir stratejinin optimize edilmesi ve "Balance + max Sharpe Ratio" kriteriyle sonuçların karşılaştırılması makalesinde anlatılmıştır.

//+------------------------------------------------------------------+
//|                                              OnTester_Sample.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "OnTester() yöneticisine sahip örnek uzman danışman"
#property description "Özel bir optimizasyon kriteri olarak, "
#property description "karesel sapma hatasına bölünen"
#property description "denge grafiği doğrusal regresyon oranı geri döndürülür"
//--- alım-satım operasyonları için sınıfı dahil et
#include <Trade\Trade.mqh>
//--- Uzman danışman girdi parametreleri
input double Lots               = 0.1;     // Hacim
input int    Slippage           = 10;      // İzin verilebilir kayma
input int    MovingPeriod       = 80;      // Hareket eden ortalama zaman aralığı
input int    MovingShift        = 6;       // Hareket eden ortalama kaydırma miktarı
//--- global değişkenler
int    IndicatorHandle=0;  // gösterge yönetimi
bool   IsHedging=false;    // hesabın bayrağı
CTrade trade;              // alım-satım işlemelerini gerçekleştirmek için
//--- 
#define EA_MAGIC 18052018
//+------------------------------------------------------------------+
//| Pozisyon açma durumunu kontrol et                                |
//+------------------------------------------------------------------+
void CheckForOpen(void)
  {
   MqlRates rt[2];
//--- sadece yeni bir barın başlangıcıyla işlem yap
   if(CopyRates(_Symbol,_Period,0,2,rt)!=2)
     {
      Print("CopyRates of ",_Symbol," başarısız oldu, geçmiş yok");
      return;
     }
//--- tik hacmi
   if(rt[1].tick_volume>1)
      return;
//--- hareketli ortalama değerlerini elde et
   double   ma[1];
   if(CopyBuffer(IndicatorHandle,0,1,1,ma)!=1)
     {
      Print("iMA'dan CopyBuffer başarısız oldu, veri yok");
      return;
     }
//--- sinyal varlığını kontrol et
   ENUM_ORDER_TYPE signal=WRONG_VALUE;
//--- mum daha yüksekten açıldı ancak hareketli ortalamanın altında kapandı
   if(rt[0].open>ma[0] && rt[0].close<ma[0])
      signal=ORDER_TYPE_BUY;    // alış sinyali
   else // mum daha düşükten açıldı ancak hareketli ortalamanın üzerinde kapandı
     {
      if(rt[0].open<ma[0] && rt[0].close>ma[0])
         signal=ORDER_TYPE_SELL;// satış sinyali
     }
//--- ek kontroller
   if(signal!=WRONG_VALUE)
     {
      if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100)
        {
         double price=SymbolInfoDouble(_Symbol,signal==ORDER_TYPE_SELL ? SYMBOL_BID:SYMBOL_ASK);
         trade.PositionOpen(_Symbol,signal,Lots,price,0,0);
        }
     }
//---
  }
//+------------------------------------------------------------------+
//| Pozisyon kapatma durumunu kontrol et                             |
//+------------------------------------------------------------------+
void CheckForClose(void)
  {
   MqlRates rt[2];
//--- sadece yeni bir barın başlangıcıyla işlem yap
   if(CopyRates(_Symbol,_Period,0,2,rt)!=2)
     {
      Print("CopyRates of ",_Symbol," başarısız oldu, geçmiş yok");
      return;
     }
   if(rt[1].tick_volume>1)
      return;
//--- hareketli ortalama değerlerini elde et
   double   ma[1];
   if(CopyBuffer(IndicatorHandle,0,1,1,ma)!=1)
     {
      Print("iMA'dan CopyBuffer başarısız oldu, veri yok");
      return;
     }
//--- pozisyon daha önceden PositionSelect() kullanılarak seçilmişti
   bool signal=false;
   long type=PositionGetInteger(POSITION_TYPE);
//--- mum daha yüksekten açıldı ancak hareketli ortalamanın altında kapandı - satış pozisyonunu kapat
   if(type==(long)POSITION_TYPE_SELL && rt[0].open>ma[0] && rt[0].close<ma[0])
      signal=true;
//--- mum daha düşükten açıldı ancak hareketli ortalamanın üzerinde kapandı - alış pozisyonunu kapat
   if(type==(long)POSITION_TYPE_BUY && rt[0].open<ma[0] && rt[0].close>ma[0])
      signal=true;
//--- ek kontroller
   if(signal)
     {
      if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100)
         trade.PositionClose(_Symbol,Slippage);
     }
//---
  }
//+-------------------------------------------------------------------+
//| Hesabın türünü göz önünde bulundurarak bir pozisyonu seç: Netting veya Hedging 
//+-------------------------------------------------------------------+
bool SelectPosition()
  {
   bool res=false;
//--- Hedging hesabı için bir pozisyon seç
   if(IsHedging)
     {
      uint total=PositionsTotal();
      for(uint i=0; i<total; i++)
        {
         string position_symbol=PositionGetSymbol(i);
         if(_Symbol==position_symbol && EA_MAGIC==PositionGetInteger(POSITION_MAGIC))
           {
            res=true;
            break;
           }
        }
     }
//--- Netting hesabı için bir pozisyon seç
   else
     {
      if(!PositionSelect(_Symbol))
         return(false);
      else
         return(PositionGetInteger(POSITION_MAGIC)==EA_MAGIC); //---Magix numarasını kontrol et
     }
//--- gerçekleşim sonucu
   return(res);
  }
//+------------------------------------------------------------------+
//| Uzman danışman başlatma fonksiyonu                               |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//--- alım-satım tipini seç: Netting yada Hedging
   IsHedging=((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
//--- doğru pozisyon kontrolü için bir nesne başlatın
   trade.SetExpertMagicNumber(EA_MAGIC);
   trade.SetMarginMode();
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetDeviationInPoints(Slippage);
//--- Hareketli Ortalama göstergesini oluşturun
   IndicatorHandle=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE);
   if(IndicatorHandle==INVALID_HANDLE)
     {
      printf("iMA göstergesini oluştururken hata");
      return(INIT_FAILED);
     }
//--- tamam
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Uzman danışman tik fonksiyonu                                    |
//+------------------------------------------------------------------+
void OnTick(void)
  {
//--- eğer bir pozisyon halihazırda açıksa, kapatma durumunu kontrol et
   if(SelectPosition())
      CheckForClose();
// pozisyon açma durumunu kontrol et
   CheckForOpen();
//---
  }
//+------------------------------------------------------------------+
//| Tester fonksiyonu                                                |
//+------------------------------------------------------------------+
double OnTester()
  {
//--- özel kriter optimizasyon değeri (daha yüksek, daha iyidir)
   double ret=0.0;
//--- alım-satım işlem sonuçlarını diziye aktar
   double array[];
   double trades_volume;
   GetTradeResultsToArray(array,trades_volume);
   int trades=ArraySize(array);
//--- eğer 10'dan az işlem varsa, test pozitif sonuç vermez 
   if(trades<10)
      return (0);
//--- işlem başına ortalama sonuç
   double average_pl=0;
   for(int i=0;i<ArraySize(array);i++)
      average_pl+=array[i];
   average_pl/=trades;
//--- tek-test modu için mesaj görüntüle
   if(MQLInfoInteger(MQL_TESTER) && !MQLInfoInteger(MQL_OPTIMIZATION))
      PrintFormat("%s: İşlemler=%d, Ortalama kar=%.2f",__FUNCTION__,trades,average_pl);
//--- kar grafiği için doğrusal regresyon oranlarını hesapla
   double a,b,std_error;
   double chart[];
   if(!CalculateLinearRegression(array,chart,a,b))
      return (0);
//--- regresyon hattından grafik sapmasının hatasını hesapla
   if(!CalculateStdError(chart,a,b,std_error))
      return (0);
//--- trend karlarının standart sapmaya oranını hesapla
   ret=(std_error == 0.0) ? a*trades : a*trades/std_error;
//--- özel kriter optimizasyon değerini geri döndür
   return(ret);
  }
//+------------------------------------------------------------------+
//| İşlemlerden karlar/zararların dizisini elde et                   |
//+------------------------------------------------------------------+
bool GetTradeResultsToArray(double &pl_results[],double &volume)
  {
//--- tüm işlem geçmişini talep et
   if(!HistorySelect(0,TimeCurrent()))
      return (false);
   uint total_deals=HistoryDealsTotal();
   volume=0;
//--- dizinin başlangıç boyutunu kenarlık ile ayarla - geçmişteki işlemlerin sayısına göre
   ArrayResize(pl_results,total_deals);
//--- işlem sonucunu düzelten, işlemlerin sayacı - kar yada zarar
   int counter=0;
   ulong ticket_history_deal=0;
//--- tüm işlemlerden geç
   for(uint i=0;i<total_deals;i++)
     {
      //--- bir işlem seç 
      if((ticket_history_deal=HistoryDealGetTicket(i))>0)
        {
         ENUM_DEAL_ENTRY deal_entry  =(ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket_history_deal,DEAL_ENTRY);
         long            deal_type   =HistoryDealGetInteger(ticket_history_deal,DEAL_TYPE);
         double          deal_profit =HistoryDealGetDouble(ticket_history_deal,DEAL_PROFIT);
         double          deal_volume =HistoryDealGetDouble(ticket_history_deal,DEAL_VOLUME);
         //--- sadece alım-satım operasyonlarıyla ilgileniyoruz        
         if((deal_type!=DEAL_TYPE_BUY) && (deal_type!=DEAL_TYPE_SELL))
            continue;
         //--- sadece karları/zararları düzelten işlemler
         if(deal_entry!=DEAL_ENTRY_IN)
           {
            //--- işlem sonuçlarını diziye yaz ve işlemlerin sayacını arttır
            pl_results[counter]=deal_profit;
            volume+=deal_volume;
            counter++;
           }
        }
     }
//--- dizinin son boyutunu ayarla
   ArrayResize(pl_results,counter);
   return (true);
  }
//+------------------------------------------------------------------+
//| Doğrusal regresyonu hesapla y=a*x+b                              |
//+------------------------------------------------------------------+
bool CalculateLinearRegression(double  &change[],double &chartline[],
                               double  &a_coef,double  &b_coef)
  {
//--- veri yeterliliğini kontrol et
   if(ArraySize(change)<3)
      return (false);
//--- bir birikim ile bir grafik dizisi oluştur
   int N=ArraySize(change);
   ArrayResize(chartline,N);
   chartline[0]=change[0];
   for(int i=1;i<N;i++)
      chartline[i]=chartline[i-1]+change[i];
//--- şimdi, regresyon oranlarını hesapla
   double x=0,y=0,x2=0,xy=0;
   for(int i=0;i<N;i++)
     {
      x=x+i;
      y=y+chartline[i];
      xy=xy+i*chartline[i];
      x2=x2+i*i;
     }
   a_coef=(N*xy-x*y)/(N*x2-x*x);
   b_coef=(y-a_coef*x)/N;
//---
   return (true);
  }
//+------------------------------------------------------------------+
//|  Belirli a ve b için ortalama karesel sapma hatası               |
//+------------------------------------------------------------------+
bool  CalculateStdError(double  &data[],double  a_coef,double  b_coef,double &std_err)
  {
//--- karesel hataların toplamı
   double error=0;
   int N=ArraySize(data);
   if(N<=2)
      return (false);
   for(int i=0;i<N;i++)
      error+=MathPow(a_coef*i+b_coef-data[i],2);
   std_err=MathSqrt(error/(N-2));
//--- 
   return (true);
  }

Ayrıca bakınız

Alım-satım stratejilerini sınama, TesterHideIndicators, Optimizasyon sonuçlarıyla çalışma, TesterStatistics, OnTesterInit, OnTesterDeinit, OnTesterPass, MQL_TESTER, MQL_OPTIMIZATION, FileOpen, FileWrite, FileLoad, FileSave