初学者的问题 MQL5 MT5 MetaTrader 5 - 页 1469

 
Alexey Viktorov #:

Artyom,有时候任何人都会累,都会犯错。不要认为这是批评,但rates_total - Bars() 总是零...

一般来说,很难向这个角色解释。USE 的受害者...

句中的 rates_totalBars()rates_total is 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 #:

你认为我写错了什么?请说明理由。

在我和你的例子中,循环的极限是什么?

那么Buffer0 就应该像在时间序列ArraySetAsSeries(Buffer0,true) 中那样进行索引;否则示例就不清楚了

 

总的来说,是这样的。我认为,从零开始计算过去的指标是不太正确的。

我总是从过去计算到现在。这里,我们在收盘价上画一条线:

//+------------------------------------------------------------------+
//|                                                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 指标 中,在您反转索引 之前 ,零柱就是过去。


附注:Artem 又说对了。我用错了术语,我应该写 "零指数",而不是 "零柱"。

 
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);
 }
//+------------------------------------------------------------------+