초보자의 질문 MQL5 MT5 MetaTrader 5 - 페이지 1469

 
Alexey Viktorov #:

아티옴, 가끔은 누구나 지쳐서 실수를 할 때가 있습니다. 비판이라고 생각하지 마세요. rates_total - Bars()는 항상 0입니다...

일반적으로이 캐릭터에 대해 설명하기가 매우 어렵습니다. 사용의 희생자 ...

문장에서 rates_total은 Bars() 입니다.즉,rates_total은 Bars()입니다.

아래 코드에서

int limit=Bars()-prev_calculated;

그가 맞습니다.

저는 USE를 사용하지 않았습니다. 그때는 존재하지 않았습니다.
 
Novichokkk 수를 포함하며 차트에서 사용할 수 있는 막대 수에 해당합니다.

예를 들어 MA5, 5개의 막대 평균은 100개의 막대를 표시하도록 설정합니다. 그러면 인디케이터는 104번째 막대에서 100번째 막대까지 계산하고 100번째 막대부터 선 그리기를 시작합니다. 그러면 rates_total=5, 100 또는 104?

   int limit = rates_total-100;

   if(prev_calculated <= 0)
     {
      // Вот тут нужно инициализировать все буферы пустыми значениями
      ArrayInitialize(Buffer0, EMPTY_VALUE);
      ArrayInitialize(Buffer1, 0);
      ArrayInitialize(Buffer2, clrNONE);
      // ... и т.д.
     }
   else
      limit = prev_calculated - 1;

//--- Экономный просчёт индикатора
   for(int i = limit; i < rates_total; i++)
     {
      Buffer0[i] = (/*что-то там*/);
     }

따라서 계산은 마지막 100개 막대에서 수행됩니다.

 
Aleksandr Slavskii #:

본질적으로 rates_total과 Bars() 같은 것이고, Bars()만 함수이므로 이를 호출하면 rates_total 변수 값을 읽는 것보다 실행 시간 측면에서 더 많은 비용이 소요됩니다.

명확하게 설명해 주셔서 감사합니다.

 
Artyom Trishkin #:

제가 뭘 잘못 썼다고 생각하시나요? 정정해 주세요.

저와 여러분의 예시에서 한계는 무엇이며 루프가 어디에서 오는지 설명해 주세요.

그렇다면 시계열 ArraySetAsSeries(Buffer0,true) 에서와 같이 Buffer0이 인덱싱되어야 하며, 그렇지 않으면 예제가 명확하지 않습니다.

 

일반적으로 다음과 같습니다. 제로 바에서 과거까지 지표를 계산하는 것은 그다지 정확하지 않다고 생각합니다.

저는 항상 과거에서 현재로 계산합니다. 여기에서는 종가에 선을 그려 보겠습니다:

//+------------------------------------------------------------------+
//|                                                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 지표에서는 인덱싱을 반전할 때까지 제로 바는 과거입니다.


추신 : 아르템이 다시 옳습니다. "제로 바" 대신 "제로 인덱스"라고 써야 했는데 잘못된 용어를 사용했습니다.

 
Aleksandr Slavskii #:

감사합니다. 매우 상세한 답변입니다!

초보자를 또 헷갈리게 하네요)

MQL5 인디케이터에서는 인덱싱을 반전하기 전까지는 제로 막대가 과거입니다.

충분한 답변을 드린 것 같습니다. 약간 다른 두 가지 지표를 첨부했습니다. 의미를 본 신규 이민자는 정상적인 노년층으로 성장한 다음 스스로 힌트를 줄 것입니다. 그리고 누가 혼란 스러울 것입니다-음, 그것은 "필요한 것이 무엇입니까?"를 의미합니다.

모든 버퍼, 인덱싱이 배포되었습니다. 그리고 제로 막대가 차트에 있습니다. 표시기에서 그려진 버퍼 (및 계산 된 버퍼에서도)에는 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                   //  Тут понятно. Задаём  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);
 }
//+------------------------------------------------------------------+
사유: