English 中文 Español Deutsch 日本語 Português
Самостоятельная оценка результатов тестирования эксперта

Самостоятельная оценка результатов тестирования эксперта

MetaTrader 4Тестер | 31 мая 2006, 16:04
5 079 5
Slava
Slava

    Сначала несколько слов о порядке тестирования. Перед началом тестирования тестовая подсистема загружает эксперта, выставляет ему назначенные пользователем параметры и вызывает функцию init(). Затем тестер "проигрывает" сгенерированную последовательность и каждый раз вызывает функцию start(). Когда тестовая последовательность заканчивается, тестер вызывает функцию deinit(). При этом в эксперте доступна вся история совершенных сделок, сформированная в процессе тестирования. В этот момент и можно проанализировать работу эксперта.

    Представленная ниже функция CalculateSummary обеспечивает расчет результатов тестирования - тех самых данных, которые представлены в стандартном отчете тестера стратегий.
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;
  }

    Для правильного расчета необходимо знать значение начального депозита. Для этого в функции init() необходимо вызвать функцию AccountBalance(), которая отдаст значение баланса на момент начала тестирования.
void init()
  {
   ExtInitialDeposit=AccountBalance();
  }
    В представленной выше функции CalculateSummary, как и в стандартном отчете, прибыль считается в валюте депозита. И другие результаты торговли, такие как "наибольшая прибыльная сделка" или "максимальный непрерывный убыток", которые рассчитываются на основе прибыли, также имеют денежную единицу измерения. Если мы захотим считать прибыль в пунктах, то это очень просто сделать.
...
      //---- market orders only
      if(type!=OP_BUY && type!=OP_SELL) continue;
      //---- calculate profit in points
      profit=(OrderClosePrice()-OrderOpenPrice())/MarketInfo(OrderSymbol(),MODE_POINT);
      SummaryProfit+=profit;
...

    Полученные результаты можно вывести в файл отчета при помощи функции 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);
  }

    Пример формирования отчета приведен ниже.
void deinit()
  {
   if(!IsOptimization())
     {
      if(!IsTesting()) ExtInitialDeposit=CalculateInitialDeposit();
      CalculateSummary(ExtInitialDeposit);
      WriteReport("MACD_Sample_Report.txt");
     }
  }

    Как видно в представленном примере, отчеты можно формировать не только после тестирования, но и при деинициализации реально работающего советника. Возникает вопрос, как же узнать размер начального депозита, если в клиентский терминал может быть подгружена не вся история сделок. Функция CalculateInitialDeposit позволяет решить этот вопрос.
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);
  }

    Именно таким способом формируются отчеты в клиентском терминале MetaTrader 4.



  
    Можно сравнить с данными, рассчитанными самостоятельно.

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)

    Приложенный к статье файл SummaryReport.mq4 рекомендуется разместить в директории experts\include и подключать его при помощи директивы #include.
#include <SummaryReport.mq4>
 
double ExtInitialDeposit;


Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (5)
[Удален] | 21 апр. 2007 в 16:05

Покажите подключение к советнику на простом примере пожалуйста

Евгений
Евгений | 22 дек. 2007 в 00:27
Полезно.
[Удален] | 12 мар. 2008 в 17:39
Хорошая штука!!!
Nefedov Kirill
Nefedov Kirill | 6 апр. 2010 в 21:25
Параметры AbsoluteDrawdown, MaxDrawdown, MaxDrawdownPercent, RelDrawdownPercent, RelDrawdown считаются неправильно, не совпадают они с отчётом тестера build 226
[Удален] | 19 июл. 2010 в 20:24
А зачем вообще нужно дублировать отчет тестера? Какой практический смысл изложеного?
Работа с файлами. Пример визуализация важных рыночных событий Работа с файлами. Пример визуализация важных рыночных событий
Статья рассматривает перспективу использования MQL4 для более продуктивной работы на рынке ФОРЕКС.
События в МetaТrader 4 События в МetaТrader 4
Статья посвящена программному отслеживанию событий в терминале МetaТrader 4, таких как открытие, закрытие и модификация ордеров, и рассчитана на пользователя, обладающего базовыми навыками работы с терминалом и программирования на MQL 4.
Графический эксперт AutoGraf Графический эксперт AutoGraf
Рассматриваются возможности использования графических средств для создания удобного интерфейса управления торговлей.
MagicNumber - "магический" идентификатор ордера MagicNumber - "магический" идентификатор ордера
Статья посвящена бесконфликтной торговле нескольких экспертов на одном терминале МТ 4. Она научит эксперта управлять только "своими" ордерами, не модифицируя и не закрывая "чужие" (открытые вручную или другими экспертами) позиции. Статья рассчитана на пользователя, обладающего базовыми навыками работы с терминалом и программирования на MQL 4.