初心者の方からの質問 MQL5 MT5 MetaTrader 5 - ページ 1469

 
Alexey Viktorov #:

アルチョム、誰でも疲れてミスをすることはある。批判とは思わないでほしいが、rates_total - Bars()は常にゼロに なる...。

一般的に、このキャラクターに説明するのは非常に難しい。USEの犠牲者...

文中の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 #:

私が間違ったことを書いたと思いますか?正当化してください。

私の例とあなたの例では、ループはどこから来るのですか?

Well, then Buffer0 should be indexed as in the time seriesArraySetAsSeries(Buffer0,true); otherwise the example is not clear.

 

一般的にはこんな感じです。指標をゼロバーから過去に向かって計算するのは、あまり正しくないと思います。

私はいつも過去から現在までを計算します。ここで、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の インジケーターでは、インデックスを逆に するまでは 、ゼロバーが過去になります。


追伸:Artemの言う通りです。ゼロバー」ではなく「ゼロインデックス」と書くべきでした。

 
Aleksandr Slavskii #:

ありがとう。素晴らしい詳細な回答だ!

また初心者を混乱させている)

MQL5の インジケーターでは、インデックスを逆に するまでは 、ゼロバーが過去になります。

網羅的にお答えしたつもりです。少し違う2つのインジケーターを添付しました。その意味を知った新参者は、普通の古参者に成長し、そして自らヒントを出すだろう。そして、誰が迷うのか--まあ、それは「何が必要なのか」ということだ。

私はすべてのバッファとそのインデックスを配置している。ゼロ・バーはチャート上にある。インジケータでは、描画されたバッファには(そして計算されたバッファにも)ゼロの配列インデックスしかありません。初心者が混乱しないように、チャート上のゼロ・バーとインジケータのバッファ配列のゼロ・インデックスが一致するようにしたいのです。

 

コードが正しいことを祈りつつ、コードを書いてみた。もしそれが正しければ、他の誰かの助けになるかもしれない。

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