Вопросы от начинающих MQL5 MT5 MetaTrader 5 - страница 1469

 
Aleksandr Slavskii #:

По своей сути  rates_total и Bars()  это одно и то же, только Bars() это функция, соответственно её вызов обойдётся дороже по времени выполнения, чем прочесть значение переменной  rates_total.

Спасибо, доходчиво. 

 
Artyom Trishkin #:

А что, Вы считаете, я написал не так? Обоснуйте, пожалуйста.

Чему равен limit, и откуда цикл будет в моём и Вашем примере.

Ну тогда нужно  Buffer0 задать индексацию как в тайм серии  ArraySetAsSeries(Buffer0,true); иначе пример получается не понятным.

 

В общем вот так. Считаю, что индикаторы рассчитывать от нулевого бара в прошлое - не очень верно.

Всегда их считаю из прошлого в настоящее. Вот, нарисуем линию по Close:

//+------------------------------------------------------------------+
//|                                                TestCalculate.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot BufferClose
#property indicator_label1  "BufferClose"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- indicator buffers
double         BufferClose[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,BufferClose,INDICATOR_DATA);
   ArraySetAsSeries(BufferClose,true);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(time,true);
   string txt="";
   int limit=rates_total-prev_calculated;
   
//--- Первый запуск или изменение исторических данных
   if(limit>1)
     {
      limit=rates_total-1;
      txt=StringFormat
        ("Первый запуск или изменение исторических данных. \nrates_total=%ld, prev_calculated=%ld, rates_total-prev_calculated=%ld, limit=%ld\nИнициализируем буфер",
         rates_total,prev_calculated,rates_total-prev_calculated,limit
        );
      Print(txt);
      ArrayInitialize(BufferClose,EMPTY_VALUE);
     }
     
//--- Новый бар
   if(limit==1)
     {
      txt="Новый бар, рассчитываем 2 бара - первый и нулевой";
      PrintFormat("%s. %s, limit=%ld",(string)time[0],txt,limit);
     }
     
//--- Текущий бар
   if(limit==0)
     {
      txt=StringFormat
        ("Рассчитываем текущий бар. rates_total=%ld, prev_calculated=%ld, rates_total-prev_calculated=%ld, limit=%ld",
         rates_total,prev_calculated,rates_total-prev_calculated,limit
        );
     }
   Comment(txt);
     
//--- Основной цикл
   for(int i=limit;i>=0;i--)
     {
      BufferClose[i]=close[i];
     }
     
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
 

С указанием количества просчитываемых баров:

//+------------------------------------------------------------------+
//|                                                TestCalculate.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot BufferClose
#property indicator_label1  "BufferClose"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- input parameters
input int InpBarsCalc = 100;  /* Number of calculated bars */  // Количество рассчитываемых баров
//--- indicator buffers
double         BufferClose[];
//--- global variables
int calc_total;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,BufferClose,INDICATOR_DATA);
   ArraySetAsSeries(BufferClose,true);
   calc_total=(InpBarsCalc<1 ? 1 : InpBarsCalc);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- indicator buffers mapping
   Comment("");
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(time,true);
   string txt="";
   int limit=rates_total-prev_calculated;
   
//--- Первый запуск или изменение исторических данных
   if(limit>1)
     {
      limit=rates_total-1;
      txt=StringFormat
        ("Первый запуск или изменение исторических данных. \nrates_total=%ld, prev_calculated=%ld, rates_total-prev_calculated=%ld, limit=%ld\nИнициализируем буфер",
         rates_total,prev_calculated,rates_total-prev_calculated,limit
        );
      Print(txt);
      ArrayInitialize(BufferClose,EMPTY_VALUE);
     }
     
//--- Новый бар
   if(limit==1)
     {
      txt="Новый бар, рассчитываем 2 бара - первый и нулевой";
      PrintFormat("%s. %s, limit=%ld",(string)time[0],txt,limit);
     }
     
//--- Текущий бар
   if(limit==0)
     {
      txt=StringFormat
        ("Рассчитываем текущий бар. rates_total=%ld, prev_calculated=%ld, rates_total-prev_calculated=%ld, limit=%ld",
         rates_total,prev_calculated,rates_total-prev_calculated,limit
        );
     }
   Comment(txt);
     
//--- Количество просчитываемых баров (начало расчёта)
   int total=fmin(limit,calc_total);
//--- Основной цикл
   for(int i=total;i>=0;i--)
     {
      BufferClose[i]=close[i];
     }
     
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
 
Artyom Trishkin #:

С указанием количества просчитываемых баров:

Спасибо. Отличный развёрнутый ответ!

В общем вот так. Считаю, что индикаторы рассчитывать от нулевого бара в прошлое - не очень верно.

Вы опять запутываете новичков)

В индикаторах MQL5, пока не развернёшь индексацию в обратном направлении нулевой бар это прошлое. 


P.S. Артём опять прав. Я применил не правильный термин, вместо "нулевой бар" нужно было написать "нулевой индекс".

 
Aleksandr Slavskii #:

Спасибо. Отличный развёрнутый ответ!

Вы опять запутываете новичков)

В индикаторах MQL5, пока не развернёшь индексацию в обратном направлении нулевой бар это прошлое. 

Я вроде бы дал исчерпывающий ответ. Два индикатора прикрепил чуть отличающихся. Новичок, увидевший смысл - вырастет в нормального старожилу, и ещё сам потом подскажет. А кто запутается - ну так значит "а оно надо?"

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

 

Попробовал расписать код, надеюсь правильно. Возможно кому тоже поможет, если правильно.

//+------------------------------------------------------------------+
//|                                                TestCalculate.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window                //
#property indicator_buffers 1                   //  Тут понятно. Задаём  1 буфер и одно отображение.
#property indicator_plots   1                   //  На самом графике.


//--- plot BufferClose
#property indicator_label1  "BufferClose"       //     
#property indicator_type1   DRAW_LINE           //
#property indicator_color1  clrRed              //  Тут тоже понятно. Свойства для описания отображаемой линии.
#property indicator_style1  STYLE_SOLID         //  Название, линия, цвет, стиль, толщина. 
#property indicator_width1  1                   //  Если бы было 2 таких #property indicator_plots   2,
                                                //  то было бы 2 таких блока, на каждую линию отдельно. И бувера 2 минимум,
                                                //  если не нужно промежуточных вычислений без вывода.




//--- indicator buffers
double         BufferClose[];                   //  Объявляем динамический массив типа double ,
                                                //  в который в будущем будем скидывать значения типа double,
                                                //  которые и будут являться значениями, по которым будет строиться индикатор,
                                                //  в данном случае линия.


//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()                                    //  функция инициализации программы
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,BufferClose,INDICATOR_DATA);//  соединяем индикаторный буфер с нашим объявленным массивом, 
                                                //  так как индикаторный буфер у нас всего 1, это буде первый,
                                                //  а индексация у него будет начинаться с 0 как в массиве, далее передаём в функцию
                                                //  название самого массива, из которого будем брать значения-BufferClose,
                                                //  далее устанавливаем INDICATOR_DATA что говори о том что 
                                                //  данный буфер не для промежуточных вычислений, который не нужно выводить
                                                //  на экран. А именно то, что это отображаемый буфер.
   
   
   ArraySetAsSeries(BufferClose,true);          //  Поскольку направление индексации у всех массивов и индикаторах буферов
                                                //  не совпадает с направлением индексации тайм серий (а работать мы будем с ними),
                                                //  то функцией ArraySetAsSeries меняем направление индексации,
                                                //  передаём название нашего массива, и вторым значением 
                                                //  устанавливаем true, что означает что нумерация в массиве теперь будет производиться 
                                                //  в обратном порядке, вернее так же как и в  массивах тайм серий.
//---
   return(INIT_SUCCEEDED);                      // оператор return возвращает нам что инициализация прошла успешно
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,          //  инициализируем (вызываем) функцию обработки событий есть 2-х видов, на основе обработки массивов,
                const int prev_calculated,      //  и на основе обработки тайм серий (наш вариант). Потому в функцию передаём перечень
                const datetime &time[],         //  массивов тайм серий, к которым можно будет обращаться внутри функции. 
                const double &open[],           //  Не забываем массивы в функцию передаются по ссылкам  (&).
                const double &high[],           //  Так же передаём в функцию 2 параметра. 
                const double &low[],            //  rates_total Размер массива price[] или входных тайм серий, доступных индикатору для расчета
                const double &close[],          //  Наш вариант для входных тайм серий, поэтому Во втором варианте функции значение параметра
                const long &tick_volume[],      //  соответствует количеству баров на графике, на котором он запущен.
                const long &volume[],           //  Второй prev_calculated Содержит значение, которое вернула функция OnCalculate() на предыдущем вызове. 
                const int &spread[])            //  Предназначено для пропуска в расчетах тех баров, которые не изменились с предыдущего запуска этой функции.
  {
//---
   ArraySetAsSeries(close,true);                //
   ArraySetAsSeries(time,true);//               //  Переданные в функцию массивы отражают ценовые данные, т.е. эти массивы имеют признак тайм серии 
                                                //  и функция ArrayIsSeries() вернет true при проверке этих массивов. Но тем не менее,
                                                //  направление индексации необходимо в любом случае проверять только функцией ArrayGetAsSeries().
                                                //  Чтобы не зависеть от умолчаний, необходимо безусловно вызывать функцию
                                                //  ArraySetAsSeries() для тех массивов, с которыми предполагается работать, 
                                                //  и установить требуемое направление индексации.
                                                //  В нашем случае вызовем её для массивов close и time.
   
   
   
   
   string txt="";                               //  Объявим строковую переменную txt и присвоим ей пустое значение .
   int limit=rates_total-prev_calculated;       //  Так как пример учебный, она нам понадобится для наглядного выведения далее информации.
                                                //  Так же объявим целочисленную переменную limit и присвоим ей значение rates_total-prev_calculated.
                                                //  Она нам будет нужна в дальнейшем для работы в цикле, в котором мы будем проходиться циклом и 
                                                //  записывать полученные значения цен в созданный массив, от которого потом и будет 
                                                //  строиться линия индикатора.
                                                
   
//--- Первый запуск или изменение исторических данных
   if(limit>1)
     {
      limit=rates_total-1;
      txt=StringFormat
        ("Первый запуск или изменение исторических данных. \nrates_total=%ld, prev_calculated=%ld, rates_total-prev_calculated=%ld, limit=%ld\nИнициализируем буфер",
         rates_total,prev_calculated,rates_total-prev_calculated,limit
        );
      Print(txt);
      ArrayInitialize(BufferClose,EMPTY_VALUE);
     }
  
                                                 //  При первом запуске, или смене исторических данных, prev_calculated=0,
                                                 //  так как предыдущего вызова ещё не было, это первый.  
                                                 //  Поэтому вводим проверку  if(limit>1), и если это правда, то это значит,
                                                 //  что обсчета ещё не было, и цикл начнём с переменной limit.
                                                 //  При этом последний элемент индикаторного буфера будет заполняться с индексом rates_total-1
                                                 //  Далее (потом в конце, когда пойдём по циклу) 
                                                 //  в основном цикле заполнится массив именно этим размером rates_total.
                                                 //  
                                                 //  А пока инициализируем наш массив пустым.
  
     
//--- Новый бар
   if(limit==1)
     {
      txt="Новый бар, рассчитываем 2 бара - первый и нулевой";
      PrintFormat("%s. %s, limit=%ld",(string)time[0],txt,limit);
     }
                                                 //  Это условие  для того,  чтобы показать изменение
                                                 //  переменной limit на каждом шаге.
                                                 //  В данном случае пришёл новый бар, то есть 
                                                 //  rates_total увеличился на один бар, а prev_calculated стал равен прошлому rates_total,
                                                 //  то есть limit=rates_total-prev_calculated=1
                                                 //  то есть цикл будет с 1 до 0, то есть в массив пишем не все прошлые значения, 
                                                 //  а 2 последних на 0-м и 1-м баре (поскольку массивы мы развернули это несформированный 
                                                 //  первый бар на графике, и второй за ним) обсчет идет 2-х последних баров.
      
      
//--- Текущий бар
   if(limit==0)
     {
      txt=StringFormat
        ("Рассчитываем текущий бар. rates_total=%ld, prev_calculated=%ld, rates_total-prev_calculated=%ld, limit=%ld",
         rates_total,prev_calculated,rates_total-prev_calculated,limit
        );
     }
   Comment(txt);
                                                  //  То же самое, промежуточный показ данных на следующем шаге.
                                                  //  Поскольку на текущем баре цена меняется, он ещё не сформирован.
                                                  //  То получается что он не входит в новый rates_total,
                                                  //  Но при этом обрабатывается, так как цена меняется.
                                                  //  В этом случае несформированный бар внутри бара будет иметь
                                                  //  limit=rates_total-prev_calculated=0, то есть rates_total=prev_calculated
                                                  //  то есть обсчитывается только нулевой бар.
   
   
   
     
//--- Основной цикл
   for(int i=limit;i>=0;i--)                      //  Это цикл, который после каждой новой обработки событий
                                                  //  работает с массивом, но при первом запуске заполняет его весь
                                                  //  Размеров в данном случае rates_total.
                                                  //  А при последующих обработках обсчитывает и добавляет в него только 
                                                  //  2 последних бара.
     {
      BufferClose[i]=close[i];
     }
     
//--- return value of prev_calculated for next call
   return(rates_total);                           //  В конце программы обработчику событий нужно вернуть
                                                  //  значение rates_total, чтобы при следующей обработке 
                                                  //  обсчёт вёлся с учётом этой информации. Атак как при первой обработке 
                                                  //  значение prev_calculated равно 0, то при следующем обращении  
                                                  //  prev_calculated будет равно этому возвращённому значению.
                                                  
  }
//+------------------------------------------------------------------+

                                                  // Примечание.  rates_total Размер массива price[] или входных тайм серий, 
                                                  // доступных индикатору для расчета. При том если прокрутить историю графика глубоко назад, 
                                                  // то история подгружается, но при этом это не первый запуск индикатора.
                                                  // Получается что rates_total увеличивается при этом  prev_calculated сбрасывается на 0,
                                                  // как при первом запуске.
 

Мне очень не нравится переворачивание индексации буферов. Поэтому я решил показать альтернативный вариант индикатора

//+------------------------------------------------------------------+
//|                                                TestCalculate.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot BufferClose
#property indicator_label1  "BufferClose"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDarkOrchid
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
//--- indicator buffers
double         BufferClose[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
 {
//--- indicator buffers mapping
  SetIndexBuffer(0,BufferClose); //,INDICATOR_DATA); INDICATOR_DATA — по умолчанию. Поэтому это можно не писать.
//---
  return(INIT_SUCCEEDED);
 }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
 {
//---
  string txt="";
  int limit=rates_total-prev_calculated;
//--- Первый запуск или изменение исторических данных
  if(limit>1)
   {
    limit=rates_total-1;
    txt=StringFormat
        ("Первый запуск или изменение исторических данных. \nrates_total=%ld, prev_calculated=%ld, rates_total-prev_calculated=%ld, limit=%ld\nИнициализируем буфер",
         rates_total,prev_calculated,rates_total-prev_calculated,limit
        );
    Print(txt);
//--- Заполнять индикаторный буфер пустыми значениями желательно, но не обязательно если индикатор не DRAW_ARROW или DRAW_COLOR_ARROW.
    ArrayInitialize(BufferClose,EMPTY_VALUE); 
//--- Обнулять исключительно когда prev_calculated == 0 или как в этом случае rates_total-prev_calculated > 1
   }

//--- Новый бар
  if(limit==1)
   {
    txt="Новый бар, рассчитываем 2 бара - первый и нулевой";
    PrintFormat("%s. %s, limit=%ld",(string)time[0],txt,limit);
   }

//--- Текущий бар
  if(limit==0)
   {
    txt=StringFormat
        ("Рассчитываем текущий бар. rates_total=%ld, prev_calculated=%ld, rates_total-prev_calculated=%ld, limit=%ld",
         rates_total,prev_calculated,rates_total-prev_calculated,limit
        );
   }
  Comment(txt);
//--- ВСЁ ОТ СТРОКИ №44 И ВКЛЮЧАЯ ЭТУ СТРОКУ НОСИТ ИСКЛЮЧИТЕЛЬНО ИНФОРМАЦИОННЫЙ ХАРАКТЕР И В РАБОЧЕМ ИНДИКАТОРЕ СОВЕРШЕННО НЕОБЯЗАТЕЛЬНО.


//---
  if(prev_calculated > 0 && rates_total-prev_calculated > 1)
    return 0; // Если во время работы индикатора подгрузились новые бары, запустим пересчёт индикатора с нуля…
//--- Основной цикл
//--- Пересчитаем индикатор или от начала истории (от нулевого бара) или только последний бар
  int i=fmax(0, prev_calculated-1);
  while(i < rates_total && !IsStopped())
   {
    BufferClose[i]=close[i];
    i++;
   }

//--- return value of prev_calculated for next call
  return(rates_total);
 }
//+------------------------------------------------------------------+
 

Добрый день!

Не могли бы вы подсказать, как в тестере стратегий скопировать дневные цены из будущего.

Скажем, робот закончил работу в день D. Нужно скачать дневные цены по дням D+1, D+2, ..., D+60 (естественно, эти все дни в прошлом).

Хотелось бы использовать что-то вроде:

MqlRates DayRate[];                          // Будет содержать цены, объемы и спред для каждого дневного бара

ArraySetAsSeries(DayRate,true);

CopyRates(_Symbol,PERIOD_D1,60,60,DayRate);  // Получить исторические месячные данные за 60 дней в будущем


С уважением, Александр


 
klycko #:

Добрый день!

Не могли бы вы подсказать, как в тестере стратегий скопировать дневные цены из будущего.

Скажем, робот закончил работу в день D. Нужно скачать дневные цены по дням D+1, D+2, ..., D+60 (естественно, эти все дни в прошлом).

Хотелось бы использовать что-то вроде:

MqlRates DayRate[];                          // Будет содержать цены, объемы и спред для каждого дневного бара

ArraySetAsSeries(DayRate,true);

CopyRates(_Symbol,PERIOD_D1,60,60,DayRate);  // Получить исторические месячные данные за 60 дней в будущем


С уважением, Александр


А в реальной торговле вам не нужны цены из будущего?

Причина обращения: