OnTester

La funzione viene chiamata nell' Expert Advisors quando si verifica l'evento Tester per eseguire le azioni necessarie dopo il test.

double  OnTester(void);

Valore di ritorno

Valore dell'ottimizzazione del criterio personalizzato per la valutazione dei risultati del test.  

Nota

La funzione OnTester() può essere utilizzata solo durante il test di EA ed è intesa principalmente per il calcolo di un valore che viene utilizzato come criterio "Custom max" quando si ottimizzano i parametri di input.

Durante l'ottimizzazione genetica, i risultati di smistamento all'interno di una generazione vengono eseguiti in ordine decrescente. Ciò significa che i risultati con il valore più alto sono considerati i migliori dal punto di vista del criterio di ottimizzazione. I valori peggiori per tale ordinamento vengono collocati alla fine e vengono successivamente scartati. Pertanto, non prendono parte alla formazione della prossima generazione.

Pertanto, la funzione OnTester() consente non solo di creare e salvare i propri report dei risultati dei test, ma anche di controllare il processo di ottimizzazione per trovare i migliori parametri della strategia di trading.

Di seguito vi è un esempio di calcolare l'ottimizzazione del criterio personalizzato. L'idea è di calcolare la regressione lineare del grafico del bilancio. È descritto nell'articolo >Optimizing a strategy using balance graph and comparing results with "Balance + max Sharpe Ratio" criterion".

//+------------------------------------------------------------------+
//|                                              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 "Esempio di EA con l'handler OnTester()"
#property description "Come criterio di ottimizzazione personalizzato, "
#property description "il rapporto tra la regressione lineare del grafico di equilibrio"
#property description "diviso per l'errore quadratico medio di deviazione restituito"
//--- include la classe per le operazioni di trading
#include <Trade\Trade.mqh>
//--- Parametri di input EA
input double Lots               = 0.1;     // Volume
input int    Slippage           = 10;      // Slippage ammissibile
input int    MovingPeriod       = 80;      // Periodo Media Mobile
input int    MovingShift        = 6;       // Slittamento Media Mobile
//--- variabili globali
int    IndicatorHandle=0;  // handle indicatore
bool   IsHedging=false;    // flag dell'account
CTrade trade;              // per eseguiore operazioni di trade
//--- 
#define EA_MAGIC 18052018
//+------------------------------------------------------------------+
//| Verifica le condizioni di apertura della posizione               |
//+------------------------------------------------------------------+
void CheckForOpen(void)
  {
   MqlRates rt[2];
//--- trade solo all'inizio di una nuova barra
   if(CopyRates(_Symbol,_Period,0,2,rt)!=2)
     {
      Print("CopyRates di ",_Symbol," fallito, nessuno storico");
      return;
     }
//--- volume tick
   if(rt[1].tick_volume>1)
      return;
//--- riceve valori media mobile
   double   ma[1];
   if(CopyBuffer(IndicatorHandle,0,1,1,ma)!=1)
     {
      Print("CopyBuffer da iMA fallito, niente dati");
      return;
     }
//--- controlla la presenza del segnale
   ENUM_ORDER_TYPE signal=WRONG_VALUE;
//--- candela aperta più in alto ma chiusa al di sotto della media mobile
   if(rt[0].open>ma[0] && rt[0].close<ma[0])
      signal=ORDER_TYPE_BUY;    // segnale buy
   else // candela aperta in basso ma chiusa al di sopra della media mobile
     {
      if(rt[0].open<ma[0] && rt[0].close>ma[0])
         signal=ORDER_TYPE_SELL;// segnale sell
     }
//--- controlli aggiuntivi
   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);
        }
     }
//---
  }
//+------------------------------------------------------------------+
//| Verifica le condizioni di chiusura della posizione               |
//+------------------------------------------------------------------+
void CheckForClose(void)
  {
   MqlRates rt[2];
//--- trade solo all'inizio di una nuova barra
   if(CopyRates(_Symbol,_Period,0,2,rt)!=2)
     {
      Print("CopyRates di ",_Symbol," fallito, nessuno storico");
      return;
     }
   if(rt[1].tick_volume>1)
      return;
//--- riceve valori media mobile
   double   ma[1];
   if(CopyBuffer(IndicatorHandle,0,1,1,ma)!=1)
     {
      Print("CopyBuffer da iMA fallito, niente dati");
      return;
     }
//--- la posizione è già stata selezionata in precedenza utilizzando PositionSelect()
   bool signal=false;
   long type=PositionGetInteger(POSITION_TYPE);
//--- candela aperta più in alto ma chiusa al di sotto della media mobile - chiude una posizione short
   if(type==(long)POSITION_TYPE_SELL && rt[0].open>ma[0] && rt[0].close<ma[0])
      signal=true;
//--- candela aperta più bassa ma chiusa sopra la media mobile - chiude una posizione long
   if(type==(long)POSITION_TYPE_BUY && rt[0].open<ma[0] && rt[0].close>ma[0])
      signal=true;
//--- controlli aggiuntivi
   if(signal)
     {
      if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100)
         trade.PositionClose(_Symbol,Slippage);
     }
//---
  }
//+-------------------------------------------------------------------+
//| Selezionare una posizione considerando il tipo di account: Netting o Hedging 
//+-------------------------------------------------------------------+
bool SelectPosition()
  {
   bool res=false;
//--- seleziona una posizione per un account Hedging
   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;
           }
        }
     }
//--- seleziona una posizione per un account Netting
   else
     {
      if(!PositionSelect(_Symbol))
         return(false);
      else
         return(PositionGetInteger(POSITION_MAGIC)==EA_MAGIC); //---controllo del Magic number
     }
//--- risultato dell'esecuzione
   return(res);
  }
//+------------------------------------------------------------------+
//| Funzione di inizializzazione Expert                              |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//--- impostazione tipo di trading: Netting o Hedging
   IsHedging=((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
//--- inizializzazione di un oggetto per il corretto controllo della posizione
   trade.SetExpertMagicNumber(EA_MAGIC);
   trade.SetMarginMode();
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetDeviationInPoints(Slippage);
//--- crea indicatore Moving Average
   IndicatorHandle=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE);
   if(IndicatorHandle==INVALID_HANDLE)
     {
      printf("Errore creazione indicatore iMA");
      return(INIT_FAILED);
     }
//--- ok
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Funzione tick Expert                                             |
//+------------------------------------------------------------------+
void OnTick(void)
  {
//--- se una posizione è già aperta, controllare le condizioni di chiusura
   if(SelectPosition())
      CheckForClose();
// controlla la condizione di apertura della posizione
   CheckForOpen();
//---
  }
//+------------------------------------------------------------------+
//| Funzione tester                                                  |
//+------------------------------------------------------------------+
double OnTester()
  {
//--- valore di ottimizzazione del criterio personalizzato (più alto è, meglio è)
   double ret=0.0;
//--- ottieni risultati di trade nell'array
   double array[];
   double trades_volume;
   GetTradeResultsToArray(array,trades_volume);
   int trades=ArraySize(array);
//--- se ci sono meno di 10 operazioni, i risultati del test non danno risultati positivi
   if(trades<10)
      return (0);
//--- risultato medio per trade
   double average_pl=0;
   for(int i=0;i<ArraySize(array);i++)
      average_pl+=array[i];
   average_pl/=trades;
//--- visualizza il messaggio per la modalità test singolo
   if(MQLInfoInteger(MQL_TESTER) && !MQLInfoInteger(MQL_OPTIMIZATION))
      PrintFormat("%s: Trades=%d, Profitto medio=%.2f",__FUNCTION__,trades,average_pl);
//--- calcola i rapporti di regressione lineare per il grafico del profitto
   double a,b,std_error;
   double chart[];
   if(!CalculateLinearRegression(array,chart,a,b))
      return (0);
//--- calcola l'errore della deviazione del chart dalla linea di regressione
   if(!CalculateStdError(chart,a,b,std_error))
      return (0);
//--- calcola il rapporto tra la tendenza profitti e la deviazione standard
   ret=(std_error == 0.0) ? a*trades : a*trades/std_error;
//--- restituisce il valore di ottimizzazione del criterio personalizzato
   return(ret);
  }
//+------------------------------------------------------------------+
//| Ottieni l'array di profitti/perdite dagli affari                 |
//+------------------------------------------------------------------+
bool GetTradeResultsToArray(double &pl_results[],double &volume)
  {
//--- richiede la cronologia completa del trading
   if(!HistorySelect(0,TimeCurrent()))
      return (false);
   uint total_deals=HistoryDealsTotal();
   volume=0;
//--- imposta la grandezza iniziale dell'array con un margine - in base al numero di affari nella cronistoria
   ArrayResize(pl_results,total_deals);
//--- contatore di affari che fissano il risultato di trading - profitti o perdite
   int counter=0;
   ulong ticket_history_deal=0;
//--- passa attraverso tutti gli affari
   for(uint i=0;i<total_deals;i++)
     {
      //--- seleziona un affare 
      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);
         //--- siamo solo interessati alle operazioni di trading        
         if((deal_type!=DEAL_TYPE_BUY) && (deal_type!=DEAL_TYPE_SELL))
            continue;
         //--- solo gli affari che fissano i profitti/perdite
         if(deal_entry!=DEAL_ENTRY_IN)
           {
            //--- scrivi il risultato del trading sull'array ed aumenta il contatore degli affari
            pl_results[counter]=deal_profit;
            volume+=deal_volume;
            counter++;
           }
        }
     }
//--- imposta la grandezza finale dell'array
   ArrayResize(pl_results,counter);
   return (true);
  }
//+------------------------------------------------------------------+
//| Calculaa la regressione lineare y=a*x+b                          |
//+------------------------------------------------------------------+
bool CalculateLinearRegression(double  &change[],double &chartline[],
                               double  &a_coef,double  &b_coef)
  {
//--- controlla la sufficienza dei dati
   if(ArraySize(change)<3)
      return (false);
//--- crea un array chart con un accumulo
   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];
//--- ora, calcola i rapporti di regressione
   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);
  }
//+------------------------------------------------------------------+
//| Calcola l'errore di deviazione quadratica media per specificati a e b     
//+------------------------------------------------------------------+
bool  CalculateStdError(double  &data[],double  a_coef,double  b_coef,double &std_err)
  {
//--- somma dei quadrati di errore
   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);
  }

Guarda anche

Test delle strategie di trading, TesterHideIndicators, Lavorare con i risultati di ottimizzazione, TesterStatistics, OnTesterInit, OnTesterDeinit, OnTesterPass, MQL_TESTER, MQL_OPTIMIZATION, FileOpen, FileWrite, fileload, FileSave