English Русский 中文 Español Deutsch 日本語
Como avaliar os resultados dos testes do Expert

Como avaliar os resultados dos testes do Expert

MetaTrader 4Testador | 18 fevereiro 2016, 11:21
1 694 0
Slava
Slava

Em primeiro lugar, algumas palavras a respeito do procedimento de teste. Antes de iniciar o teste, o subsistema de teste carrega o expert, configura os seus parâmetros definidos anteriormente pelo usuário, e chama a função init(). Então o verificador executa a sequência gerada e chama a função start() todas as vezes. Quando a sequência de teste é finalizada, o verificador chama a função deinit(). Além disso, todo o histórico de transações gerado durante o teste fica disponível. A eficiência do expert pode ser analisada nesse momento.

A função CalculateSummary abaixo fornece o cálculo dos resultados do teste, ou seja, os dados fornecidos no relatório padrão do verificador de estratégia.

void CalculateSummary(double initial_deposit)
  {
   int    sequence=0, profitseqs=0, lossseqs=0;
   double sequential=0.0, prevprofit=EMPTY_VALUE, drawdownpercent, drawdown;
   double maxpeak=initial_deposit, minpeak=initial_deposit, balance=initial_deposit;
   int    trades_total=HistoryTotal();
//---- initialize summaries
   InitializeSummaries(initial_deposit);
//----
   for(int i=0; i<trades_total; i++)
     {
      if(!OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue;
      int type=OrderType();
      //---- initial balance not considered
      if(i==0 && type==OP_BALANCE) continue;
      //---- calculate profit
      double profit=OrderProfit()+OrderCommission()+OrderSwap();
      balance+=profit;
      //---- drawdown check
      if(maxpeak<balance)
        {
         drawdown=maxpeak-minpeak;
         if(maxpeak!=0.0)
           {
            drawdownpercent=drawdown/maxpeak*100.0;
            if(RelDrawdownPercent<drawdownpercent)
              {
               RelDrawdownPercent=drawdownpercent;
               RelDrawdown=drawdown;
              }
           }
         if(MaxDrawdown<drawdown)
           {
            MaxDrawdown=drawdown;
            if(maxpeak!=0.0) MaxDrawdownPercent=MaxDrawdown/maxpeak*100.0;
            else MaxDrawdownPercent=100.0;
           }
         maxpeak=balance;
         minpeak=balance;
        }
      if(minpeak>balance) minpeak=balance;
      if(MaxLoss>balance) MaxLoss=balance;
      //---- market orders only
      if(type!=OP_BUY && type!=OP_SELL) continue;
      SummaryProfit+=profit;
      SummaryTrades++;
      if(type==OP_BUY) LongTrades++;
      else             ShortTrades++;
      //---- loss trades
      if(profit<0)
        {
         LossTrades++;
         GrossLoss+=profit;
         if(MinProfit>profit) MinProfit=profit;
         //---- fortune changed
         if(prevprofit!=EMPTY_VALUE && prevprofit>=0)
           {
            if(ConProfitTrades1<sequence ||
               (ConProfitTrades1==sequence && ConProfit2<sequential))
              {
               ConProfitTrades1=sequence;
               ConProfit1=sequential;
              }
            if(ConProfit2<sequential ||
               (ConProfit2==sequential && ConProfitTrades1<sequence))
              {
               ConProfit2=sequential;
               ConProfitTrades2=sequence;
              }
            profitseqs++;
            AvgConWinners+=sequence;
            sequence=0;
            sequential=0.0;
           }
        }
      //---- profit trades (profit>=0)
      else
        {
         ProfitTrades++;
         if(type==OP_BUY)  WinLongTrades++;
         if(type==OP_SELL) WinShortTrades++;
         GrossProfit+=profit;
         if(MaxProfit<profit) MaxProfit=profit;
         //---- fortune changed
         if(prevprofit!=EMPTY_VALUE && prevprofit<0)
           {
            if(ConLossTrades1<sequence ||
               (ConLossTrades1==sequence && ConLoss2>sequential))
              {
               ConLossTrades1=sequence;
               ConLoss1=sequential;
              }
            if(ConLoss2>sequential ||
               (ConLoss2==sequential && ConLossTrades1<sequence))
              {
               ConLoss2=sequential;
               ConLossTrades2=sequence;
              }
            lossseqs++;
            AvgConLosers+=sequence;
            sequence=0;
            sequential=0.0;
           }
        }
      sequence++;
      sequential+=profit;
      //----
      prevprofit=profit;
     }
//---- final drawdown check
   drawdown=maxpeak-minpeak;
   if(maxpeak!=0.0)
     {
      drawdownpercent=drawdown/maxpeak*100.0;
      if(RelDrawdownPercent<drawdownpercent)
        {
         RelDrawdownPercent=drawdownpercent;
         RelDrawdown=drawdown;
        }
     }
   if(MaxDrawdown<drawdown)
     {
      MaxDrawdown=drawdown;
      if(maxpeak!=0) MaxDrawdownPercent=MaxDrawdown/maxpeak*100.0;
      else MaxDrawdownPercent=100.0;
     }
//---- consider last trade
   if(prevprofit!=EMPTY_VALUE)
     {
      profit=prevprofit;
      if(profit<0)
        {
         if(ConLossTrades1<sequence ||
            (ConLossTrades1==sequence && ConLoss2>sequential))
           {
            ConLossTrades1=sequence;
            ConLoss1=sequential;
           }
         if(ConLoss2>sequential ||
            (ConLoss2==sequential && ConLossTrades1<sequence))
           {
            ConLoss2=sequential;
            ConLossTrades2=sequence;
           }
         lossseqs++;
         AvgConLosers+=sequence;
        }
      else
        {
         if(ConProfitTrades1<sequence ||
            (ConProfitTrades1==sequence && ConProfit2<sequential))
           {
            ConProfitTrades1=sequence;
            ConProfit1=sequential;
           }
         if(ConProfit2<sequential ||
            (ConProfit2==sequential && ConProfitTrades1<sequence))
           {
            ConProfit2=sequential;
            ConProfitTrades2=sequence;
           }
         profitseqs++;
         AvgConWinners+=sequence;
        }
     }
//---- collecting done
   double dnum, profitkoef=0.0, losskoef=0.0, avgprofit=0.0, avgloss=0.0;
//---- average consecutive wins and losses
   dnum=AvgConWinners;
   if(profitseqs>0) AvgConWinners=dnum/profitseqs+0.5;
   dnum=AvgConLosers;
   if(lossseqs>0)   AvgConLosers=dnum/lossseqs+0.5;
//---- absolute values
   if(GrossLoss<0.0) GrossLoss*=-1.0;
   if(MinProfit<0.0) MinProfit*=-1.0;
   if(ConLoss1<0.0)  ConLoss1*=-1.0;
   if(ConLoss2<0.0)  ConLoss2*=-1.0;
//---- profit factor
   if(GrossLoss>0.0) ProfitFactor=GrossProfit/GrossLoss;
//---- expected payoff
   if(ProfitTrades>0) avgprofit=GrossProfit/ProfitTrades;
   if(LossTrades>0)   avgloss  =GrossLoss/LossTrades;
   if(SummaryTrades>0)
     {
      profitkoef=1.0*ProfitTrades/SummaryTrades;
      losskoef=1.0*LossTrades/SummaryTrades;
      ExpectedPayoff=profitkoef*avgprofit-losskoef*avgloss;
     }
//---- absolute drawdown
   AbsoluteDrawdown=initial_deposit-MaxLoss;
  }

Para que os cálculos estejam corretos, o valor do depósito inicial deve ser conhecido. Para isso, na função init(), a função AccountBalance() deve ser chamada, pois ela irá fornecer o valor do balanço no início do teste.

void init()
  {
   ExtInitialDeposit=AccountBalance();
  }

Na função CalculateSummary acima, assim como no relatório padrão, o lucro é calculado na moeda de depósito. Outros resultados de transações, como a "transação de maior lucro" ou a "perda máxima consecutiva", que são calculados com base no lucro, também são medidos em termos monetários. Portanto, é fácil recalcular o lucro em pontos.

...
      //---- market orders only
      if(type!=OP_BUY && type!=OP_SELL) continue;
      //---- calculate profit in points
      profit=(OrderClosePrice()-OrderOpenPrice())/MarketInfo(OrderSymbol(),MODE_POINT);
      SummaryProfit+=profit;
...

Os resultados obtidos podem ser escritos no arquivo do relatório através do uso da função WriteReport.

void WriteReport(string report_name)
  {
   int handle=FileOpen(report_name,FILE_CSV|FILE_WRITE,'\t');
   if(handle<1) return;
//----
   FileWrite(handle,"Initial deposit           ",InitialDeposit);
   FileWrite(handle,"Total net profit          ",SummaryProfit);
   FileWrite(handle,"Gross profit              ",GrossProfit);
   FileWrite(handle,"Gross loss                ",GrossLoss);
   if(GrossLoss>0.0)
      FileWrite(handle,"Profit factor             ",ProfitFactor);
   FileWrite(handle,"Expected payoff           ",ExpectedPayoff);
   FileWrite(handle,"Absolute drawdown         ",AbsoluteDrawdown);
   FileWrite(handle,"Maximal drawdown          ",
                     MaxDrawdown,
                     StringConcatenate("(",MaxDrawdownPercent,"%)"));
   FileWrite(handle,"Relative drawdown         ",
                     StringConcatenate(RelDrawdownPercent,"%"),
                     StringConcatenate("(",RelDrawdown,")"));
   FileWrite(handle,"Trades total                 ",SummaryTrades);
   if(ShortTrades>0)
      FileWrite(handle,"Short positions(won %)    ",
                        ShortTrades,
                        StringConcatenate("(",100.0*WinShortTrades/ShortTrades,"%)"));
   if(LongTrades>0)
      FileWrite(handle,"Long positions(won %)     ",
                        LongTrades,
                        StringConcatenate("(",100.0*WinLongTrades/LongTrades,"%)"));
   if(ProfitTrades>0)
      FileWrite(handle,"Profit trades (% of total)",
                        ProfitTrades,
                        StringConcatenate("(",100.0*ProfitTrades/SummaryTrades,"%)"));
   if(LossTrades>0)
      FileWrite(handle,"Loss trades (% of total)  ",
                        LossTrades,
                        StringConcatenate("(",100.0*LossTrades/SummaryTrades,"%)"));
   FileWrite(handle,"Largest profit trade      ",MaxProfit);
   FileWrite(handle,"Largest loss trade        ",-MinProfit);
   if(ProfitTrades>0)
      FileWrite(handle,"Average profit trade      ",GrossProfit/ProfitTrades);
   if(LossTrades>0)
      FileWrite(handle,"Average loss trade        ",-GrossLoss/LossTrades);
   FileWrite(handle,"Average consecutive wins  ",AvgConWinners);
   FileWrite(handle,"Average consecutive losses",AvgConLosers);
   FileWrite(handle,"Maximum consecutive wins (profit in money)",
                     ConProfitTrades1,
                     StringConcatenate("(",ConProfit1,")"));
   FileWrite(handle,"Maximum consecutive losses (loss in money)",
                     ConLossTrades1,
                     StringConcatenate("(",-ConLoss1,")"));
   FileWrite(handle,"Maximal consecutive profit (count of wins)",
                     ConProfit2,
                     StringConcatenate("(",ConProfitTrades2,")"));
   FileWrite(handle,"Maximal consecutive loss (count of losses)",
                     -ConLoss2,
                     StringConcatenate("(",ConLossTrades2,")"));
//----
   FileClose(handle);
  }

Um exemplo do uso dessas funções para gerar um relatório é fornecido abaixo.

void deinit()
  {
   if(!IsOptimization())
     {
      if(!IsTesting()) ExtInitialDeposit=CalculateInitialDeposit();
      CalculateSummary(ExtInitialDeposit);
      WriteReport("MACD_Sample_Report.txt");
     }
  }

Como você pode ver, os relatórios podem ser gerados não apenas após o teste, mas também na deinicialização do Expert Advisor ativo. Você pode estar se perguntando como é possível descobrir o tamanho do depósito inicial caso o histórico da conta tiver sido transferido apenas parcialmente ao terminal (por exemplo, caso apenas o histórico relativo a um mês tiver sido solicitado na aba do histórico da conta). A função CalculateInitialDeposit auxilia na resolução desse problema.

double CalculateInitialDeposit()
  {
   double initial_deposit=AccountBalance();
//----
   for(int i=HistoryTotal()-1; i>=0; i--)
     {
      if(!OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue;
      int type=OrderType();
      //---- initial balance not considered
      if(i==0 && type==OP_BALANCE) break;
      if(type==OP_BUY || type==OP_SELL)
        {
         //---- calculate profit
         double profit=OrderProfit()+OrderCommission()+OrderSwap();
         //---- and decrease balance
         initial_deposit-=profit;
        }
      if(type==OP_BALANCE || type==OP_CREDIT)
         initial_deposit-=OrderProfit();
     }
//----
   return(initial_deposit);
  }

Essa é a forma com que os relatórios são gerados no terminal do cliente do MetaTrader 4.




Ela pode ser comparada aos dados calculados através do uso do programa exposto.

Initial deposit             10000
Total net profit            -13.16
Gross profit                20363.32
Gross loss                  20376.48
Profit factor               0.99935416
Expected payoff             -0.01602923
Absolute drawdown           404.28
Maximal drawdown            1306.36 (11.5677%)
Relative drawdown           11.5966%    (1289.78)
Trades total                    821
Short positions(won %)      419 (24.821%)
Long positions(won %)       402 (31.592%)
Profit trades (% of total)  231 (28.1364%)
Loss trades (% of total)    590 (71.8636%)
Largest profit trade        678.08
Largest loss trade          -250
Average profit trade        88.15290043
Average loss trade          -34.53640678
Average consecutive wins    1
Average consecutive losses  4
Maximum consecutive wins (profit in money)  4   (355.58)
Maximum consecutive losses (loss in money)  15  (-314.74)
Maximal consecutive profit (count of wins)  679.4   (2)
Maximal consecutive loss (count of losses)  -617.16 (8)

Recomenda-se colocar o arquivo SummaryReport.mq4 anexo a este artigo no diretório experts\include e inseri-lo através do uso da diretiva #include.

#include <SummaryReport.mq4>
 
double ExtInitialDeposit;


Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1403

Arquivos anexados |
Gerenciamento de pedidos - É simples Gerenciamento de pedidos - É simples
O artigo trata das várias formas de se controlar posições abertas e pedidos pendentes. Ele tem como objetivo simplificar a escrita de Expert Advisors.
Eventos no MetaTrader 4 Eventos no MetaTrader 4
O artigo trata do monitoramento programado de eventos no terminal do cliente do MetaTrader 4, como a abertura/fechamento/modificação de pedidos, e é destinado a um usuário que possua habilidades básicas no trabalho com o terminal e em programação em MQL4.
Armazenamento e visualização de informações Armazenamento e visualização de informações
O artigo trata de métodos convenientes e eficientes de armazenamento e visualização de informações. São consideradas aqui alternativas ao arquivo log padrão do terminal e à função Comment().
Sincronização de Expert Advisors, scripts e indicadores Sincronização de Expert Advisors, scripts e indicadores
O artigo considera a necessidade e os princípios gerais do desenvolvimento de um pacote de programas que incluiria um Expert Advisor, um script e um indicador.