Novichokkk número de barras disponíveis para o indicador para cálculo e corresponde ao número de barras disponíveis no gráfico.
Por exemplo, MA5, média de 5 barras, definida para exibir 100 barras. Então, o indicador calculará da 104ª até a 100ª barra e começará a desenhar a linha a partir da 100ª barra. Então, rates_total=5, ou 100, ou 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] = (/*что-то там*/);
}
Portanto, o cálculo será feito nas últimas 100 barras.
Em essência, rates_total e Bars() são a mesma coisa, masBars() é uma função, portanto, chamá-la custará mais em termos de tempo de execução do que ler o valor da variável rates_total.
Muito obrigado. Essa é uma ótima resposta detalhada!
Você está confundindo os novatos novamente)
Nos indicadoresMQL5, até que você reverta a indexação, a barra zero é o passado.
Acho que dei uma resposta exaustiva. Anexei dois indicadores que são ligeiramente diferentes. Um recém-chegado que tenha visto o significado se tornará um veterano normal, e então ele mesmo dará dicas. E quem estiver confuso - bem, isso significa "o que é necessário?".
Eu tenho todos os buffers, sua indexação, implantados. E a barra zero está no gráfico. No indicador, em seu buffer desenhado (e no calculado também), só pode haver um índice de matriz zero. Prefiro que a barra zero no gráfico coincida com o índice zero da matriz do buffer do indicador, para que os iniciantes não se confundam.
Tentei descrever o código, espero que esteja correto. Talvez ele também ajude outras pessoas, se estiver correto.
//+------------------------------------------------------------------+//| 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_buffers1// Тут понятно. Задаём 1 буфер и одно отображение.#property indicator_plots1// На самом графике.//--- plot BufferClose#property indicator_label1"BufferClose"// #property indicator_type1DRAW_LINE//#property indicator_color1clrRed// Тут тоже понятно. Свойства для описания отображаемой линии.#property indicator_style1STYLE_SOLID// Название, линия, цвет, стиль, толщина. #property indicator_width11// Если бы было 2 таких #property indicator_plots 2,// то было бы 2 таких блока, на каждую линию отдельно. И бувера 2 минимум,// если не нужно промежуточных вычислений без вывода.//--- indicator buffersdouble BufferClose[]; // Объявляем динамический массив типа double ,// в который в будущем будем скидывать значения типа double,// которые и будут являться значениями, по которым будет строиться индикатор,// в данном случае линия.//+------------------------------------------------------------------+//| Custom indicator initialization function |//+------------------------------------------------------------------+intOnInit() // функция инициализации программы
{
//--- indicator buffers mappingSetIndexBuffer(0,BufferClose,INDICATOR_DATA);// соединяем индикаторный буфер с нашим объявленным массивом, // так как индикаторный буфер у нас всего 1, это буде первый,// а индексация у него будет начинаться с 0 как в массиве, далее передаём в функцию// название самого массива, из которого будем брать значения-BufferClose,// далее устанавливаем INDICATOR_DATA что говори о том что // данный буфер не для промежуточных вычислений, который не нужно выводить// на экран. А именно то, что это отображаемый буфер.ArraySetAsSeries(BufferClose,true); // Поскольку направление индексации у всех массивов и индикаторах буферов// не совпадает с направлением индексации тайм серий (а работать мы будем с ними),// то функцией ArraySetAsSeries меняем направление индексации,// передаём название нашего массива, и вторым значением // устанавливаем true, что означает что нумерация в массиве теперь будет производиться // в обратном порядке, вернее так же как и в массивах тайм серий.//---return(INIT_SUCCEEDED); // оператор return возвращает нам что инициализация прошла успешно
}
//+------------------------------------------------------------------+//| Custom indicator iteration function |//+------------------------------------------------------------------+intOnCalculate(constint rates_total, // инициализируем (вызываем) функцию обработки событий есть 2-х видов, на основе обработки массивов,constint prev_calculated, // и на основе обработки тайм серий (наш вариант). Потому в функцию передаём переченьconstdatetime &time[], // массивов тайм серий, к которым можно будет обращаться внутри функции. constdouble &open[], // Не забываем массивы в функцию передаются по ссылкам (&).constdouble &high[], // Так же передаём в функцию 2 параметра. constdouble &low[], // rates_total Размер массива price[] или входных тайм серий, доступных индикатору для расчетаconstdouble &close[], // Наш вариант для входных тайм серий, поэтому Во втором варианте функции значение параметраconstlong &tick_volume[], // соответствует количеству баров на графике, на котором он запущен.constlong &volume[], // Второй prev_calculated Содержит значение, которое вернула функция OnCalculate() на предыдущем вызове. constint &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 callreturn(rates_total); // В конце программы обработчику событий нужно вернуть
// значение rates_total, чтобы при следующей обработке
// обсчёт вёлся с учётом этой информации. Атак как при первой обработке
// значение prev_calculated равно 0, то при следующем обращении
// prev_calculated будет равно этому возвращённому значению.
}
//+------------------------------------------------------------------+// Примечание. rates_total Размер массива price[] или входных тайм серий, // доступных индикатору для расчета. При том если прокрутить историю графика глубоко назад, // то история подгружается, но при этом это не первый запуск индикатора.// Получается что rates_total увеличивается при этом prev_calculated сбрасывается на 0,// как при первом запуске.
Eu realmente não gosto do rollover da indexação do buffer. Por isso, decidi mostrar uma variante alternativa do indicador
//+------------------------------------------------------------------+//| 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_buffers1#property indicator_plots1//--- plot BufferClose#property indicator_label1"BufferClose"#property indicator_type1DRAW_LINE#property indicator_color1clrDarkOrchid#property indicator_style1STYLE_SOLID#property indicator_width12//--- indicator buffersdouble BufferClose[];
//+------------------------------------------------------------------+//| Custom indicator initialization function |//+------------------------------------------------------------------+intOnInit()
{
//--- indicator buffers mappingSetIndexBuffer(0,BufferClose); //,INDICATOR_DATA); INDICATOR_DATA — по умолчанию. Поэтому это можно не писать.//---return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+//| Custom indicator iteration function |//+------------------------------------------------------------------+intOnCalculate(constint rates_total,
constint prev_calculated,
constdatetime &time[],
constdouble &open[],
constdouble &high[],
constdouble &low[],
constdouble &close[],
constlong &tick_volume[],
constlong &volume[],
constint &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)
return0; // Если во время работы индикатора подгрузились новые бары, запустим пересчёт индикатора с нуля…//--- Основной цикл//--- Пересчитаем индикатор или от начала истории (от нулевого бара) или только последний барint i=fmax(0, prev_calculated-1);
while(i < rates_total && !IsStopped())
{
BufferClose[i]=close[i];
i++;
}
//--- return value of prev_calculated for next callreturn(rates_total);
}
//+------------------------------------------------------------------+
Artyom, às vezes qualquer pessoa fica cansada e comete erros. Não considere isso uma crítica, mas rates_total - Bars() sempre será zero...
Em geral, é muito difícil explicar a esse personagem. Uma vítima do USE...
rates_total éBars() na frase. Ou seja,rates_total é Bars().
No código abaixo
Ele está certo.
Eu não peguei o USE. Ele não existia na época.Por exemplo, MA5, média de 5 barras, definida para exibir 100 barras. Então, o indicador calculará da 104ª até a 100ª barra e começará a desenhar a linha a partir da 100ª barra. Então, rates_total=5, ou 100, ou 104?
Portanto, o cálculo será feito nas últimas 100 barras.
Em essência, rates_total e Bars() são a mesma coisa, masBars() é uma função, portanto, chamá-la custará mais em termos de tempo de execução do que ler o valor da variável rates_total.
Obrigado, isso está muito claro.
O que você acha que eu escrevi errado? Justifique, por favor.
Qual é o limite e de onde virá o loop no meu e no seu exemplo.
Bem, então o Buffer0 deve ser indexado como na série temporal ArraySetAsSeries(Buffer0,true); caso contrário, o exemplo não está claro.
Em geral, é assim. Acho que não é muito correto calcular indicadores da barra zero até o passado.
Eu sempre os calculo do passado para o presente. Aqui, vamos desenhar uma linha no Close:
Especificando o número de barras a serem contadas:
Especificando o número de barras a serem contadas:
Obrigado. Essa é uma ótima resposta detalhada!
É o seguinte. Acho que não é muito correto calcular indicadores da barra zero para trás no tempo.
Você está confundindo os iniciantes novamente)
Nos indicadores MQL5, até que você reverta a indexação, a barra zero é o passado.
P.S. Artem está certo novamente. Eu usei o termo errado, em vez de "barra zero" eu deveria ter escrito "índice zero".
Muito obrigado. Essa é uma ótima resposta detalhada!
Você está confundindo os novatos novamente)
Nos indicadores MQL5, até que você reverta a indexação, a barra zero é o passado.
Acho que dei uma resposta exaustiva. Anexei dois indicadores que são ligeiramente diferentes. Um recém-chegado que tenha visto o significado se tornará um veterano normal, e então ele mesmo dará dicas. E quem estiver confuso - bem, isso significa "o que é necessário?".
Eu tenho todos os buffers, sua indexação, implantados. E a barra zero está no gráfico. No indicador, em seu buffer desenhado (e no calculado também), só pode haver um índice de matriz zero. Prefiro que a barra zero no gráfico coincida com o índice zero da matriz do buffer do indicador, para que os iniciantes não se confundam.
Tentei descrever o código, espero que esteja correto. Talvez ele também ajude outras pessoas, se estiver correto.
Eu realmente não gosto do rollover da indexação do buffer. Por isso, decidi mostrar uma variante alternativa do indicador