English 中文 Español Deutsch 日本語 Português
Перенос кода индикатора в код эксперта. Заключение

Перенос кода индикатора в код эксперта. Заключение

MetaTrader 4Примеры | 6 марта 2007, 14:10
2 628 11
Nikolay Kositsin
Nikolay Kositsin

Введение

На примерах предыдущих статей (Перенос кода индикатора в код эксперта. Строение индикатора. и Перенос кода индикатора в код эксперта. Общие схемы строения эксперта и индикаторных функций.) мы достаточно подробно изучили общую схему написания индикаторной функции на основе имеющегося в наличии кода индикатора и определили ее взаимосвязь с экспертом. Теперь настало время переделать и код реального эксперта, чем мы, собственно говоря, сейчас и займемся.


Исходный код эксперта

Итак, у нас в наличии имеется следующий код эксперта:

//+------------------------------------------------------------------+
//|                                         ASCTrend1RAVI_Expert.mq4 |
//|                             Copyright © 2006,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//---- ВХОДНЫЕ ПАРАМЕТРЫ
extern int RAVI_Timeframe = 240;
extern int ASCT_Timeframe = 1440;
//---- ФИЛЬТР НАПРАВЛЕНИЯ РАСЧЁТОВ СДЕЛОК
extern int Buy_Sell_Custom = 2; //0-Buy, 1-Sell, 2-Buy+Sell
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ BUY СДЕЛОК 
extern double Money_Management_Up=0.1;
extern int RISK_Up = 3;
extern int Period1_Up = 7; 
extern int Period2_Up = 65; 
extern int MA_Metod_Up = 0;
extern int PRICE_Up = 0;
extern int STOPLOSS_Up = 50;
extern int TAKEPROFIT_Up = 100;
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ SELL СДЕЛОК 
extern double Money_Management_Dn = 0.1;
extern int RISK_Dn = 3;
extern int Period1_Dn = 7; 
extern int Period2_Dn = 65; 
extern int MA_Metod_Dn = 0;
extern int PRICE_Dn = 0;
extern int STOPLOSS_Dn = 50;
extern int TAKEPROFIT_Dn = 100;
//---- Эмулированные индикаторные буферы
double RAVI_Up[3];
double RAVI_Dn[3];
double ASCTrend1_Up[2];
double ASCTrend1_Dn[2];
//+------------------------------------------------------------------+
//| Custom Expert functions                                          |
//+------------------------------------------------------------------+
#include <Lite_EXPERT.mqh>
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
//---- 
   if(RAVI_Timeframe != 1)
       if(RAVI_Timeframe != 5)
           if(RAVI_Timeframe != 15)
               if(RAVI_Timeframe != 30)
                   if(RAVI_Timeframe != 60)
                       if(RAVI_Timeframe != 240)
                           if(RAVI_Timeframe != 1440)
                               Print("Параметр RAVI_Timeframe не может" + 
                                     " быть равным " + RAVI_Timeframe+"!!!");
//---- 
   if(ASCT_Timeframe != 1)
       if(ASCT_Timeframe != 5)
           if(ASCT_Timeframe != 15)
               if(ASCT_Timeframe != 30)
                   if(ASCT_Timeframe != 60)
                       if(ASCT_Timeframe != 240)
                           if(ASCT_Timeframe != 1440)
                               Print("Параметр ASCT_Timeframe не может" + 
                                     " быть равным "+ASCT_Timeframe+"!!!");
//----                      
   if(RAVI_Timeframe > ASCT_Timeframe) 
       Print("Параметр ASCT_Timeframe не следует делать меньше" + 
             " чем RAVI_Timeframe");                   
//---- завершение инициализации
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- Проверка количества баров на достаточность для дальнейшего расчёта  
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Up*2 + 1 + 1)
       return(0);
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Dn*2 + 1 + 1)
       return(0);
//----
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Up, Period2_Up + 4))
       return(0);
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Dn, Period2_Dn + 4))
       return(0);
//---- Объявление переменных
   static double ASCTrend1_Trend_Up, ASCTrend1_Trend_Dn;
   static int LastBars;
   int    bar;
   bool   BUY_Sign, SELL_Sign; 
//---- ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ
   if(LastBars != iBars(NULL, RAVI_Timeframe))
       switch(Buy_Sell_Custom)
         {
           case 0: 
             {           
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Up[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Up, Period2_Up,
                                            MA_Metod_Up, PRICE_Up, 0, bar); 
               //----+
               ASCTrend1_Up[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 0, 1); 
               ASCTrend1_Up[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 1, 1); 
               //----+
               ASCTrend1_Trend_Up = ASCTrend1_Up[1] - ASCTrend1_Up[0];       
               break;
             }
           //---- 
           case 1: 
             {            
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Dn[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Dn, 
                                            Period2_Dn, MA_Metod_Dn, 
                                            PRICE_Dn, 0, bar);
               //----+              
               ASCTrend1_Dn[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 0, 1); 
               ASCTrend1_Dn[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 1, 1); 
               //----+
               ASCTrend1_Trend_Dn =ASCTrend1_Dn[1] - ASCTrend1_Dn[0];
               break;
             }
           //----
           default:
             {
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Up[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Up, 
                                            Period2_Up, MA_Metod_Up, 
                                            PRICE_Up, 0, bar); 
               //----+
               ASCTrend1_Up[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 0, 1); 
               ASCTrend1_Up[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 1, 1);
               //----+ 
               ASCTrend1_Trend_Up = ASCTrend1_Up[1] - ASCTrend1_Up[0];
               //----+             
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Dn[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Dn, 
                                            Period2_Dn, MA_Metod_Dn,
                                            PRICE_Dn, 0, bar);
               //----+             
               ASCTrend1_Dn[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 0, 1); 
               ASCTrend1_Dn[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 1, 1); 
               //----+
               ASCTrend1_Trend_Dn = ASCTrend1_Dn[1] - ASCTrend1_Dn[0];
             }
         }
//---- Инициализация переменной
   LastBars = iBars(NULL, RAVI_Timeframe);
//---- ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК
   switch(Buy_Sell_Custom)
     {
       //----
       case 0: 
         {
           if(RAVI_Up[1] - RAVI_Up[2] < 0)
               if(RAVI_Up[0] - RAVI_Up[1] > 0)
                   if(ASCTrend1_Trend_Up > 0)
                       BUY_Sign = true;
           break;
         }
       //----+ +---------------------------+ 
       case 1: 
         {
           if(RAVI_Dn[1] - RAVI_Dn[2] > 0)
               if(RAVI_Dn[0] - RAVI_Dn[1] < 0)
                   if(ASCTrend1_Trend_Dn < 0)
                       SELL_Sign = true;
           break;
         }
       //----
       default:
         {
           if(RAVI_Up[1] - RAVI_Up[2] < 0)
               if(RAVI_Up[0] - RAVI_Up[1] > 0)
                   if(ASCTrend1_Trend_Up > 0)
                       BUY_Sign = true;
           //----                 
           if(RAVI_Dn[1] - RAVI_Dn[2] > 0)
               if(RAVI_Dn[0] - RAVI_Dn[1] < 0)
                   if(ASCTrend1_Trend_Dn < 0)
                       SELL_Sign = true;
         }
     }
//---- СОВЕРШЕНИЕ СДЕЛОК
   switch(Buy_Sell_Custom)
     {
       //----
       case 0: 
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           break;
         }
       //----
       case 1: 
         {
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn,
                         TAKEPROFIT_Dn);
           break;
         }
       //----
       default:
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn,
                         TAKEPROFIT_Dn);
         }
     }
   return(0);
  }
//+------------------------------------------------------------------+

Предварительно нам следовало бы с данным экспертом познакомиться немного поближе. Эксперт предназначен для торговли на основе данных с четырёхчасового и дневного графиков выбранной трейдером валютной пары. Эксперт может открыть всего одну позицию по одной валютной паре в данном направлении торговли. Эксперт имеет два независимых алгоритма расчёта для совершения сделок покупки и продажи, поэтому при работе он может открывать встречные позиции.

Для более быстрой оптимизации эксперта в тестере стратегий он содержит внешнюю переменную Buy_Sell_Custom, при равенстве нулю которой эксперт ведёт расчет только Buy сигналов. Если переменная Buy_Sell_Custom равна единице, то происходит расчет только Sell сигналов. Если необходимы оба расчета, то следует внешнюю переменную Buy_Sell_Custom сделать равной двум. Остальные внешние переменные эксперта разбиты на две группы: для Buy и Sell сделок.

Сигналами к совершению сделок служат изменения направления движения пользовательского индикатора RAVI.mq4, а фильтром, отсекающим ложные сигналы, является направление тренда, определяемое по пользовательскому индикатору ASCTrend1.mq4. Эксперт имеет два обращения к индикатору ASCTrend1.mq4 для получения из нулевого и первого буферов индикатора исходных значений, знак разницы которых определяет направление тренда. Эксперт два обращения к индикатору RAVI.mq4, для получения двух вариантов (Buy и Sell) исходных значений для двух алгоритмов расчёта.

Я полагаю, что назначение большинства входных параметров эксперта не должно вызвать никаких вопросов. Оно понятно из их названий. Переменные MA_Metod_Up и MA_Metod_Dn определяют метод усреднения. Они могут быть любым из значений от нуля и до трёх. Переменные PRICE_Up и PRICE_Dn имеют значение ценовых констант, пределы изменения которых от нуля и до шести.

Эксперт, однозначно, не убыточный и при хорошей оптимизации показывает при всей своей простоте весьма приличные результаты. Так что, в принципе, он стоит того, чтобы уделить ему немного труда и превратить его из набора файлов во всего один самодостаточный файл. В коде эксперта используется файл Lite_EXPERT.mqh, представляющий собой две пользовательские функции, через обращения к которым эксперт совершает сделки:

//+------------------------------------------------------------------+
//|                                                  Lite_EXPERT.mqh |
//|                                   Version  January 7, 2007 Final |
//|                             Copyright © 2006,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
//---- Объявление глобальной переменной для запоминания времени 
//     последнего обращения к серверу
int LastTime; 
//+------------------------------------------------------------------+
//| OpenBuyOrder()                                                   |
//+------------------------------------------------------------------+
int OpenBuyOrder(bool BUY_Signal, double Money_Management, int STOPLOSS, 
                 int TAKEPROFIT)
  {
   if(!BUY_Signal)
       return(0); 
   if(TimeLocal() - LastTime < 11)
       return(0); 
   int total = OrdersTotal();
//---- Проверка на наличие открытой позиции по данной торговой 
//     паре в Buy направлении
   for(int ttt = total - 1; ttt >= 0; ttt--)     
       if(OrderSelect(ttt, SELECT_BY_POS, MODE_TRADES))
           if((OrderSymbol() == Symbol()) && (OrderType() == 0))
               return(0); 
//---- 
   double ask = NormalizeDouble(Ask, Digits);              
   double bid = NormalizeDouble(Bid, Digits);
//----
   if(ask == 0.0)
       return(-1);  
   if(bid == 0.0)
       return(-1);  
//----  
   double LotVel;      
   double tickVel = MarketInfo(Symbol(), MODE_TICKVALUE);    
   if(tickVel == 0)
       return(-1); 
//----
   if(Money_Management > 0)
       LotVel = tickVel*AccountEquity()*Money_Management / 10000.0;
   else 
       LotVel = -tickVel*10000*Money_Management / 10000.0;
//----
   double Lot = NormalizeDouble(LotVel, 1);  
   if(Lot < 0.1)
       return(-1);     
//----+ Открываем позицию на покупку
   double Stoploss = NormalizeDouble(bid - STOPLOSS*Point, Digits);  
   double TakeProfit = NormalizeDouble(ask + TAKEPROFIT*Point, Digits);
//----
   int ticket = OrderSend(Symbol(), OP_BUY, Lot, ask, 3, Stoploss, 
                          TakeProfit, NULL, 0, 0, CLR_NONE); 
   //----
   LastTime = TimeLocal(); 
   //----
   if(ticket > 0)
       return(1);
   else 
       return(-1);
  }  
//+------------------------------------------------------------------+
//|  OpenSellOrder()                                                 |
//+------------------------------------------------------------------+
int OpenSellOrder(bool SELL_Signal, double Money_Management, int STOPLOSS, 
                  int TAKEPROFIT)
  { 
   if(!SELL_Signal)
       return(0); 
   if(TimeLocal() - LastTime < 11)
       return(0); 
   int total = OrdersTotal();
//---- Проверка на наличие открытой позиции по данной торговой 
//     паре в Sell направлении 
   for(int kkk = total - 1; kkk >= 0; kkk--)      
       if(OrderSelect(kkk, SELECT_BY_POS, MODE_TRADES))
           if((OrderSymbol() == Symbol()) && (OrderType() == 1))
               return(0); 
//---- 
   double bid = NormalizeDouble(Bid, Digits); 
   double ask = NormalizeDouble(Ask, Digits);
//----
   if(bid == 0.0)
       return(-1); 
   if(ask == 0.0)
       return(-1); 
//----      
   double LotVel;      
   double tickVel = MarketInfo(Symbol(), MODE_TICKVALUE);
   if(tickVel == 0.0)
       return(-1);   
//----   
   if(Money_Management > 0)
       LotVel = tickVel*AccountEquity()*Money_Management / 10000.0;
   else 
       LotVel = -tickVel*10000*Money_Management / 10000.0;
//----
   double Lot = NormalizeDouble(LotVel, 1); 
   if(Lot < 0.1)
       return(-1);       
//----+ Открываем позицию на продажу 
   double Stoploss = NormalizeDouble(ask + STOPLOSS*Point, Digits);         
   double TakeProfit = NormalizeDouble(bid - TAKEPROFIT*Point, Digits);
//----   
   int ticket = OrderSend(Symbol(), OP_SELL, Lot, bid, 3, Stoploss, 
                          TakeProfit, NULL, 0, 0,CLR_NONE);
//----   
   LastTime = TimeLocal(); 
//----
   if(ticket > 0)
       return(1);
   else 
       return(-1);
  }              
//+------------------------------------------------------------------+

Мне так думается, что эти функции достаточно просты и каких-либо недоразумений с ними не должно возникнуть. Алгоритм обращения к этим функциям:

OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up);
OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn);

где в случае, если BUY_Sign=true функция OpenBuyOrder() открывает длинную позицию, а если SELL_Sign=true, то функция OpenSellOrder() открывает короткую позицию. Назначение параметров: Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up, Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn более чем понятно из их названий!


Построение индикаторной функции Get_ASCTrend1Series()

В конце прошлой статьи (Перенос кода индикатора в код эксперта. Общие схемы строения эксперта и индикаторных функций.) я уже сделал индикаторную функцию Get_RAVISeries(). Теперь она будет весьма кстати и нам остается произвести аналогичную функцию для индикатора ASCTrend1.mq4. Теперь мы сделаем на основе кода этого индикатора функцию Get_ASCTrend1Series(). Итак, начнём с внешнего осмотра кода этого индикатора:

//+------------------------------------------------------------------+
//|                                                        ASCTrend1 |
//|                                        Ramdass - Conversion only |
//+------------------------------------------------------------------+
//---- отрисовка индикатора в главном окне
#property indicator_chart_window 
//---- количество индикаторных буфферов
#property indicator_buffers 2 
//---- цвета индикатора
#property indicator_color1 Magenta
#property indicator_color2 Aqua
//---- толщина индикаторных линий
#property indicator_width1 2
#property indicator_width2 2
//---- ВХОДНЫЕ ПАРАМЕТРЫ ИНДИКАТОРА
extern int RISK = 3;
//---- индикаторные буфферы
double val1[];
double val2[];
//+------------------------------------------------------------------+
//| ASCTrend1 initialization function                                |
//+------------------------------------------------------------------+
int init()
  {
//---- Стиль исполнения графика
   SetIndexStyle(0, DRAW_HISTOGRAM, 0, 2);
   SetIndexStyle(1, DRAW_HISTOGRAM, 0, 2);
//---- 2 индикаторных буффера использованы для счёта
   SetIndexBuffer(0, val1);
   SetIndexBuffer(1, val2);
//---- установка значений индикатора, которые не будут видимы на графике
   SetIndexEmptyValue(0, 0.0);
   SetIndexEmptyValue(1, 0.0);
//---- имя для окон данных и лэйбs для субъокон
   IndicatorShortName("ASCTrend1");
   SetIndexLabel(0, "DownASCTrend1");
   SetIndexLabel(1, "UpASCTrend1");
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| ASCTrend1                                                        |
//+------------------------------------------------------------------+
int start()
  {
//---- введение переменных памяти  
   static double x1, x2;
//---- Введение переменных с плавающей точкой
   double value1, value2, value3, TrueCount, Range, AvgRange, MRO1, MRO2;
//---- Введение целых переменных и получение уже подсчитанных баров
   int MaxBar, iii, kkk, bar, value10, value11, 
       counted_bars = IndicatorCounted();
//---- проверка на возможные ошибки
   if(counted_bars < 0)
       return(-1);
//---- последний подсчитанный бар должен быть пересчитан 
   if(counted_bars > 0)
       counted_bars--;
//---- инициализация переменной
   value10 = 3 + RISK*2;
//---- проверка количества баров на достаточность для расчёта
   if((Bars <= value10) || (Bars < 10)) 
       return(0);
//---- определение номера самого старого бара, 
//     начиная с которого будет произведён полный пересчёт всех баров 
   MaxBar = Bars - 1 - value10;
//---- определение номера самого старого бара, 
//     начиная с которого будет произедён пересчёт только новых баров 
   bar = Bars - 1 - counted_bars;
//---- инициализация нуля
   if(bar >= MaxBar)
     {
       x1 = 67 + RISK;
       x2 = 33 - RISK;
       bar = MaxBar;
       //----
       for(kkk = Bars - 1; kkk >= MaxBar; kkk--) 
         {
           val1[kkk] = 0.0;
           val2[kkk] = 0.0;
         }
     }
//---- ОСНОВНОЙ ЦИКЛ РАСЧЁТА ИНДИКАТОРА
   while(bar >= 0)
     {  
       Range = 0.0;
       AvgRange = 0.0;
       for(iii = 0; iii <= 9; iii++) 
       AvgRange += MathAbs(High[bar+iii] - Low[bar+iii]);
       //----
       Range = AvgRange / 10;
       iii = 0;
       TrueCount = 0;
       while(iii < 9 && TrueCount < 1)
         {
           if(MathAbs(Open[bar+iii] - Close[bar+iii]) >= Range*2.0) 
           TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO1 = bar + iii; 
       else 
           MRO1 = -1;
       //----
       iii = 0;
       TrueCount = 0;
       while(iii < 6 && TrueCount < 1)
         {
           if(MathAbs(Close[bar+iii+3] - Close[bar+iii]) >= Range*4.6) 
           TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO2 = bar + iii; 
       else 
           MRO2 = -1;
       //----
       if(MRO1 > -1)
           value11 = 3; 
       else 
           value11 = value10;
       //----
       if(MRO2 > -1)
           value11 = 4; 
       else 
           value11 = value10;
       //----
       value2 = 100 - MathAbs(iWPR(NULL, 0, value11, bar));
       //---- 
       val1[bar] = 0;
       val2[bar] = 0;
       //---- 
       if(value2 > x1)
         {
           val1[bar] = Low [bar];
           val2[bar] = High[bar];
         }
       //---- 
       if(value2 < x2)
         {
           val1[bar] = High[bar]; 
           val2[bar] = Low [bar];
         }
       //---- 
       bar--;
     }
   return(0);
  }
//+------------------------------------------------------------------+

Я оптимизировал этот индикатор. Кому интересно, как он выглядел в оригинале, могут посмотреть файл ASCTrend1_Old!.mq4. Теперь приступим к изготовлению функции Get_ASCTrend1Series(). Для этого опять преобразуем код индикатора ASCTrend1.mq4 по предложенной мною в предыдущей статье схеме доработки его кода. В итоге имеем следующую пользовательскую функцию:
//+------------------------------------------------------------------+
//|                                          Get_ASCTrend1Series.mqh |
//|                        Copyright © 2006,        Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru |
//+------------------------------------------------------------------+
bool Get_ASCTrend1Series(int Number, string symbol,int timeframe, 
                         bool NullBarRecount, int RISK, 
                         double& InputBuffer[])    
  {
//---- получение количества всех баров графика
   int IBARS = iBars(symbol, timeframe);  
//---- Проверка количества баров на достаточность для дальнейшего расчёта
   if((IBARS < 3 + RISK*2 + 1)||(IBARS < 10))
       return(false);
//---- ЭМУЛЯЦИЯ ИНДИКАТОРНЫХ БУФЕРОВ
   if(ArraySize(InputBuffer) < IBARS)
     {
       ArraySetAsSeries(InputBuffer, false);
       //----  
       ArrayResize(InputBuffer, IBARS); 
       //----
       ArraySetAsSeries(InputBuffer, true);
     } 
//----+  введение статических переменных памяти
   static double x1[], x2[];
   static int IndCounted[]; 
//----+ изменение размеров статических переменных
   if(ArraySize(IndCounted) < Number + 1)
     {
       ArrayResize(x1, Number + 1); 
       ArrayResize(x2, Number + 1); 
       ArrayResize(IndCounted, Number + 1); 
     }
 //----+ Введение целой переменной
   int LastCountBar;
//----+ Проверка разрешения пересчёта нулевого бара
   if(!NullBarRecount)
       LastCountBar = 1;
//----+ Введение переменных с плавающей точкой 
   double value1, value2, value3, val1, val2;
   double TrueCount, Range, AvgRange, MRO1, MRO2;
//----+ Введение целых переменных и  получение уже посчитанных баров
   int MaxBar, iii, kkk, bar, value10, value11,
       counted_bars = IndCounted[Number];
//----+ Запоминание количества всех баров графика
   IndCounted[Number] = IBARS - 1;
//---- инициализация переменной
   value10 = 3 + RISK*2;
//---- определение номера самого старого бара, 
//     начиная с которого будет произедён пересчёт новых баров
   bar = IBARS - counted_bars - 1; 
//---- определение номера самого старого бара, 
//     начиная с которого будет произедён пересчёт всех баров
   MaxBar = IBARS - 1 - value10;
//---- инициализация нуля 
   if(bar > MaxBar)
     {
       bar = MaxBar;
       x1[Number] = 67 + RISK;
       x2[Number] = 33 - RISK;
       //----
       ArrayInitialize(InputBuffer, 0.0); 
     } 
//---- ОСНОВНОЙ ЦИКЛ РАСЧЁТА ИНДИКАТОРА
   while(bar >= LastCountBar)
     {  
       Range = 0.0;
       AvgRange = 0.0;
       for(iii = 0; iii <= 9; iii++) 
           AvgRange += MathAbs(iHigh(symbol, timeframe, bar + iii) -
                               iLow(symbol, timeframe, bar + iii));
       //----
       Range = AvgRange / 10;
       iii = 0;
       TrueCount = 0;
       while(iii < 9 && TrueCount < 1)
         {
           if(MathAbs(iOpen(symbol, timeframe, bar + iii) -
                      iClose(symbol, timeframe, bar + iii)) >= Range*2.0) 
               TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO1 = bar + iii; 
       else 
           MRO1 = -1;
       //----
       iii = 0;
       TrueCount = 0;
       while(iii < 6 && TrueCount < 1)
         {
           if(MathAbs(iClose(symbol, timeframe, bar + iii + 3) -
                      iClose(symbol, timeframe, bar + iii)) >= Range*4.6) 
               TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO2 = bar + iii; 
       else 
           MRO2 = -1;
       //----
       if(MRO1 > -1)
           value11 = 3; 
       else 
           value11 = value10;
       if(MRO2 > -1)
           value11 = 4; 
       else 
           value11 = value10;
       //----
       value2 = 100 - MathAbs(iWPR(symbol, timeframe, value11, bar));
       //---- 
       val1 = 0;
       val2 = 0;
       InputBuffer[bar] = 0;
       //---- 
       if(value2 > x1[Number])
         {
           val1 = iLow (symbol, timeframe, bar);
           val2 = iHigh(symbol, timeframe, bar);
         }
       if(value2 < x2[Number])
         {
           val1 = iHigh(symbol, timeframe, bar);
           val2 = iLow(symbol, timeframe, bar);
         } 
       InputBuffer[bar]=val2-val1;
       bar--;
     }
   //----+  
   return(true);
  }
//+------------------------------------------------------------------+

Разумеется, что сложность этой функции на порядок выше, чем у той, что мы рассмотрели в конце предыдущей статьи, но поскольку исходный код индикатора был сделан и оптимизирован абсолютно без ошибок, то и процесс создания такой функции не так чтобы уж очень сильно усложнился!

После этого нам бы следовало полученную функцию протестировать на предмет совпадения её значений со значениями индикатора, на основе кода которого они и была сделана. Для этого нам опять придётся сделать тестерного эксперта для функции Get_ASCTrend1Series():

//+------------------------------------------------------------------+
//|                                      Get_ASCTrend1SeriesTest.mq4 |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net/"
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА
extern bool NullBarRecount = true;
//---- индикаторные буфферы
double IndBuffer0[];
double IndBuffer1[];
double IndBuffer2[];
//+------------------------------------------------------------------+
//| Get_ASCTrend1Series() functions                                  |
//+------------------------------------------------------------------+
#include <Get_ASCTrend1Series.mqh>
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
//---- завершение инициализации
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- 
   double Ind_Velue,Resalt;
//---- 
   if(!Get_ASCTrend1Series(0, Symbol(), 0, NullBarRecount, 1, IndBuffer0))
       return(0);
   if(!Get_ASCTrend1Series(1, Symbol(), 240, NullBarRecount, 3, IndBuffer1))
       return(0);
   if(!Get_ASCTrend1Series(2, Symbol(), 1440, NullBarRecount, 5, IndBuffer2))
       return(0);
//---- получение индикаторных значений для теста 0
   Ind_Velue = iCustom(NULL, 0, "ASCTrend1", 1, 1, 2) - 
               iCustom(NULL, 0, "ASCTrend1", 1, 0, 2); 
   Resalt = IndBuffer0[2] - Ind_Velue; 
   Print("0:     " + Ind_Velue + "    " + IndBuffer0[2] + 
         "    " + Resalt + "");
//---- получение индикаторных значений для теста 1
   Ind_Velue = iCustom(NULL, 240, "ASCTrend1", 3, 1, 2) - 
               iCustom(NULL, 240, "ASCTrend1", 3, 0, 2); 
   Resalt = IndBuffer1[2] - Ind_Velue; 
   Print("H4:    " + Ind_Velue + "    " + IndBuffer1[2] + 
         "    " + Resalt + "");
//---- получение индикаторных значений для теста 2
   Ind_Velue = iCustom(NULL, 1440, "ASCTrend1", 5, 1, 2) - 
               iCustom(NULL, 1440, "ASCTrend1", 5, 0, 2); 
   Resalt = IndBuffer2[2] - Ind_Velue; 
   Print("Daily: " + Ind_Velue + "    " + IndBuffer2[2] + 
         "    " + Resalt + "");
//----
   return(0);
  }
//+------------------------------------------------------------------+

Запускаем тестирование этого эксперта в Тестере стратегий и после теста, проверив логфайл, убеждаемся, что созданная нами функция в плане заполнения значениями эмулируемого индикаторного буфера не имеет никаких отличий от того индикатора, на основе кода которого она строилась!


Итоговое преобразование кода исходного эксперта

И вот теперь наконец-то мы можем позволить себе такую возможность сделать преобразование кода эксперта, сделав замену обращений к пользовательским индикаторам на обращения к пользовательским индикаторным функциям:

//+------------------------------------------------------------------+
//|                                      NewASCTrend1RAVI_Expert.mq4 |
//|                             Copyright © 2006,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//---- ВХОДНЫЕ ПАРАМЕТРЫ
extern int RAVI_Timeframe = 240;
extern int ASCT_Timeframe = 1440;
//---- ФИЛЬТР НАПРАВЛЕНИЯ РАСЧЁТОВ СДЕЛОК
extern int Buy_Sell_Custom = 2; // 0-Buy, 1-Sell, 2-Buy+Sell
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ BUY СДЕЛОК 
extern double Money_Management_Up = 0.1;
extern int RISK_Up = 3;
extern int Period1_Up = 7; 
extern int Period2_Up = 65; 
extern int MA_Metod_Up = 0;
extern int PRICE_Up = 0;
extern int STOPLOSS_Up = 50;
extern int TAKEPROFIT_Up = 100;
//---- ВХОДНЫЕ ПАРАМЕТРЫ ЭКСПЕРТА ДЛЯ SELL СДЕЛОК 
extern double Money_Management_Dn = 0.1;
extern int RISK_Dn = 3;
extern int Period1_Dn = 7; 
extern int Period2_Dn = 65; 
extern int MA_Metod_Dn = 0;
extern int PRICE_Dn = 0;
extern int STOPLOSS_Dn = 50;
extern int TAKEPROFIT_Dn = 100;
//---- Эмулированные индикаторные буферы
double RAVI_Up[];
double RAVI_Dn[];
double ASCTrend1_Up[];
double ASCTrend1_Dn[];
//+------------------------------------------------------------------+
//| Get_ASCTrend1Series functions()                                  |
//+------------------------------------------------------------------+
#include <Get_ASCTrend1Series.mqh>
//+------------------------------------------------------------------+
//| Get_RAVISeries functions()                                       |
//+------------------------------------------------------------------+
#include <Get_RAVISeries.mqh>
//+------------------------------------------------------------------+
//| Custom Expert functions                                          |
//+------------------------------------------------------------------+
#include <Lite_EXPERT.mqh>
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
//---- 
   if(RAVI_Timeframe != 1)
       if(RAVI_Timeframe != 5)
           if(RAVI_Timeframe != 15)
               if(RAVI_Timeframe != 30)
                   if(RAVI_Timeframe != 60)
                       if(RAVI_Timeframe != 240)
                           if(RAVI_Timeframe != 1440)
                               Print("Параметр RAVI_Timeframe не может" + 
                                     " быть равным " +
                                     RAVI_Timeframe + "!!!");
//---- 
   if(ASCT_Timeframe != 1)
       if(ASCT_Timeframe != 5)
           if(ASCT_Timeframe != 15)
               if(ASCT_Timeframe != 30)
                   if(ASCT_Timeframe != 60)
                       if(ASCT_Timeframe != 240)
                           if(ASCT_Timeframe != 1440)
                               Print("Параметр ASCT_Timeframe не может" + 
                                     " быть равным "+
                                     ASCT_Timeframe+"!!!");
//----                      
   if(RAVI_Timeframe > ASCT_Timeframe) 
       Print("Параметр ASCT_Timeframe не следует делать " + 
             "меньше чем RAVI_Timeframe");                   
//---- завершение инициализации
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- Проверка количества баров на достаточность для дальнейшего расчёта  
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Up*2 + 1 + 1)
       return(0);
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Dn*2 + 1 + 1)
       return(0);
//----
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Up, Period2_Up + 4))
       return(0);
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Dn, Period2_Dn + 4))
       return(0);
//---- Объявление переменной для блокирования пересчёта нулевого бара
   static int LastBars;
//---- Объявление логических переменных для сигналов тренда
   bool   BUY_Sign, SELL_Sign;
//---- ВЫЧИСЛЕНИЕ ИНДИКАТОРНЫХ ЗНАЧЕНИЙ И ЗАГРУЗКА ИХ В БУФЕРЫ 
   if(LastBars != iBars(NULL, RAVI_Timeframe))
       switch(Buy_Sell_Custom)
         {
           case 0: 
             {
               Get_RAVISeries(0, Symbol(), RAVI_Timeframe, false, 
                              Period1_Up, Period2_Up, MA_Metod_Up,
                              PRICE_Up, RAVI_Up);
               //----
               Get_ASCTrend1Series(0, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Up, ASCTrend1_Up);  
               break;
             }
           case 1: 
             {            
               Get_RAVISeries(1, Symbol(), RAVI_Timeframe, false, 
                              Period1_Dn, Period2_Dn, MA_Metod_Dn, 
                              PRICE_Dn, RAVI_Dn);
               //----
               Get_ASCTrend1Series(1, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Dn, ASCTrend1_Dn); 
               break;
             }
           default:
             {
               Get_RAVISeries(0, Symbol(), RAVI_Timeframe, false,
                              Period1_Up, Period2_Up, MA_Metod_Up,
                              PRICE_Up, RAVI_Up);
               //----
               Get_ASCTrend1Series(0, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Up, ASCTrend1_Up); 
               //----              
               Get_RAVISeries(1, Symbol(), RAVI_Timeframe, false,
                              Period1_Dn, Period2_Dn,MA_Metod_Dn,
                              PRICE_Dn,RAVI_Dn);
               //----
               Get_ASCTrend1Series(1, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Dn, ASCTrend1_Dn); 
             }
         }
       //---- Инициализация переменной
       LastBars = iBars(NULL, RAVI_Timeframe);
       //---- ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК 
       switch(Buy_Sell_Custom)
         {
       case 0: 
         {
           if(RAVI_Up[2] - RAVI_Up[3] < 0)
               if(RAVI_Up[1] - RAVI_Up[2] > 0)
                   if(ASCTrend1_Up[1] > 0)
                       BUY_Sign = true;
           break;
         }
       case 1: 
         {
           if(RAVI_Dn[2] - RAVI_Dn[3] > 0)
               if(RAVI_Dn[1] - RAVI_Dn[2] < 0)
                   if(ASCTrend1_Dn[1] < 0)
                       SELL_Sign = true;
           break;
         }
       default:
         {
           if(RAVI_Up[2] - RAVI_Up[3] < 0)
               if(RAVI_Up[1] - RAVI_Up[2] > 0)
                   if(ASCTrend1_Up[1] > 0)
                       BUY_Sign = true;
           //----+                  
           if(RAVI_Dn[2] - RAVI_Dn[3] > 0)
               if(RAVI_Dn[1] - RAVI_Dn[2] < 0)
                   if(ASCTrend1_Dn[1] < 0)
                       SELL_Sign = true;
         }
     }
//---- СОВЕРШЕНИЕ СДЕЛОК
   switch(Buy_Sell_Custom)
     {
       case 0: 
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           break;
         }
       case 1: 
         {
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn, 
                         TAKEPROFIT_Dn);
           break;
         }
       default:
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn, 
                         TAKEPROFIT_Dn);
         }
     }
   return(0);
  }
//+------------------------------------------------------------------+

Вполне естественно, что последнее преобразование кода эксперта не обошлось без некоторых изменений внутри функции start() в блоке "ОПРЕДЕЛЕНИЕ СИГНАЛОВ ДЛЯ СДЕЛОК" по причине изменения расположения используемых в эмулированных индикаторных буферах ячеек по сравнению с исходными буферами.

Результаты тестирования итогового и исходного экспертов

Теперь мы можем загрузить полученного эксперта в Тестер стратегий и сравнить показатели его работы на истории с теми же самыми показателями того же самого эксперта до преобразования его кода. Никаких отличий торговых показателей при тестировании эксперта на истории в обоих случаях выявить не удаётся. Оба варианта эксперта выдают абсолютно одинаковые результаты тестирования, если периоды тестирования совпадают, а в экспертов в обоих случаях загруженны одни и те же значения внешних переменных и используется один и тот же метод тестирования.

Но сразу бросается в глаза, что в нашем случае итоговый эксперт во всех ситуациях работает не быстрее, а медленнее исходного эксперта!







С чем связанно увеличение времени тестирования эксперта? Можно предположить, что это как-то связанно с использованием эмуляции индикаторного режима работы буферов и возврата значений индикаторными функциями в буферы по ссылке. Конечно, можно пойти дальше, избавиться от эмуляции индикаторного режима работы буферов, кому это интересно, могут посмотреть и протестировать эксперта FastNewASCTrend1RAVI_Expert.mq4. Но и в этом случае наблюдается абсолютно аналогичная картина и этот эксперт работает столь же медленно, как и предыдущий. Напрашивается вполне закономерный вывод, что в нашем, достаточно простом случае преобразования кода с использованием абсолютно нересурсоёмких индикаторов их замена на пользовательские функции для ускорения работы эксперта абсолютно нецелосообразна!


Заключение

Для экспертов, которые используют обращения к достаточно нересурсоёмким индикаторам задача их ускорения методом замены обращений к пользовательским индикаторам на обращения к пользовательским функциям оказывается в общем случае неразрешимой! Но, если есть особое желание, сделать абсолютно автономного эксперта, состоящего из всего одного файла и избавиться от обращений к пользовательским индикаторам, то выполнить это желание всегда возможно, но для этого придётся очень серьёзно постараться, потому как сей труд совсем простым сделать никак не получится, а потому стремление идти в данном направлении должно иметь под собой вполне обоснованные, серьезные причины.

Последние комментарии | Перейти к обсуждению на форуме трейдеров (11)
Nikolay Kositsin
Nikolay Kositsin | 8 мар. 2007 в 03:50
Solandr! Я более основательно всё проверил и пришёл к такому выводу, что MetaTrader, не котором я производил оптимизацию этого эксперта просто глюкнулся! Увы, но я такое за ним уже один раз замечал, но как-то всерьёз не придал этому значения, просто заново инсталировал Метатрейдер в пустую папку и перетащил после этого в неё все конфигурационные файлы и прочую файловую мелочёвку. Теперь вот опять повторилась та же самая ситуация! Так что ваши результаты более верны, чем мой! Но к данной статье всё это всё-таки никакого отношения не имеет. С экспертом всё абсолютно нормально, в качестве учебного пособия и экспоната определённых идей построения экспертов он свою роль выполняет недурно и с таким результатом! По правде говоря, оно и не нужно выставлять в качестве учебных пособий слишком откровенных кандидатов на граали!
Иван
Иван | 8 мар. 2007 в 07:43
GODZILLA:
С экспертом всё абсолютно нормально, в качестве учебного пособия и экспоната определённых идей построения экспертов он свою роль выполняет недурно и с таким результатом! По правде говоря, оно и не нужно выставлять в качестве учебных пособий слишком откровенных кандидатов на граали!

Полностью согласен! Код эксперта успешно решает поставленные перед ним учебные задачи. Желающие могут с ним разобраться и использовать в своих разработках. Ещё раз спасибо за отличные статьи!
[Удален] | 13 янв. 2011 в 11:41

Здравствуйте !

Скажите, пожалуйста, для оптимизации и тестировании этого эксперта на H1 нужно ли менять эти данные ?:

//---- ВХОДНЫЕ ПАРАМЕТРЫ
extern int RAVI_Timeframe = 240;
extern int ASCT_Timeframe = 1440;

Если да, то почему ? А для М30 ?

[Удален] | 14 янв. 2011 в 12:47

И почему при уменьшении параметра

extern int ASCT_Timeframe = 1440;
меньше 1440 оптимизация не производится: идёт горизонтальная линия на графике оптимизации ?
[Удален] | 18 янв. 2011 в 16:38
Протестировал этого советника на GBPUSD после оптимизации за год, полгода, 3 месяца, 2 месяца, 1 месяц, неделю. Результат отрицательный. При оптимизации показывает прибыль. Как только запускаешь на тестер на другом участке истории - идёт убыток. Если поставить на GBPJPY, как у ребят в тестах ниже, но на другом куске истории - так же идёт хороший убыток.
Как разработать надежный и безопасный торговый робот на языке MQL4 Как разработать надежный и безопасный торговый робот на языке MQL4
В статье рассказывается об основных типах ошибок, которые возникают при создании и эксплуатации советника. Приводится пример создания безопасной автоматической торговой системы.
Перенос кода индикатора в код эксперта. Общие схемы строения эксперта и индикаторных функций Перенос кода индикатора в код эксперта. Общие схемы строения эксперта и индикаторных функций
Статья посвящена переносу кода индикатора в код эксперта и написанию экспертов, в которых отсутствуют обращения к пользовательским индикаторам, а весь программный код для расчёта нужных индикаторных значений находится внутри самого эксперта. В данной статье излагается общие схема изменения эксперта и идея построения индикаторной функции на основе пользовательского индикатора. Статья рассчитана на читателя, уже имеющего опыт программирования на языке MQL 4.
Точки разворота PIVOT POINTS, помогающие определить направление движения рынка Точки разворота PIVOT POINTS, помогающие определить направление движения рынка
Точка разворота (PIVOT POINT) – линия на графике цены, которая показывает дальнейшую тенденцию движения валютной пары. Если цена находится выше этой линии, то цена имеет тенденцию к росту. Если ниже, соответственно, - к падению.
Перенос кода индикатора в код эксперта. Строение индикатора. Перенос кода индикатора в код эксперта. Строение индикатора.
Статья посвящена переносу кода индикатора в код эксперта и написанию экспертов, в которых отсутствуют обращения к пользовательским индикаторам, а весь программный код для расчета нужных индикаторных значений находится внутри самого эксперта. В данной статье излагается общая схема строения индикатора, эмуляция индикаторных буферов в эксперте и замена функции IndicatorCounted(). Статья рассчитана на читателя, уже имеющего опыт программирования на языке MQL 4.