Novichokkk numero di barre disponibili all'indicatore per il calcolo e corrisponde al numero di barre disponibili sul grafico.
Ad esempio MA5, media di 5 barre, impostata per visualizzare 100 barre. L'indicatore calcolerà dalla 104a alla 100a barra e inizierà a tracciare la linea dalla 100a barra. Quindi rates_total=5, o 100, o 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] = (/*что-то там*/);
}
Quindi il calcolo verrà effettuato sulle ultime 100 barre.
In sostanza rates_total e Bars() sono la stessa cosa, solo cheBars() è una funzione, quindi chiamarla costerà di più in termini di tempo di esecuzione che leggere il valore della variabile rates_total.
Negli indicatoriMQL5, finché non si inverte l'indicizzazione, la barra zero è il passato.
Credo di aver dato una risposta esauriente. Ho allegato due indicatori che sono leggermente diversi. Un nuovo arrivato che ha visto il significato diventerà un normale veterano e darà lui stesso dei suggerimenti. E chi sarà confuso - beh, significa "cosa è necessario?".
Ho tutti i buffer, la loro indicizzazione, distribuiti. E la barra dello zero è sul grafico. Nell'indicatore, nel suo buffer disegnato (e anche in quello calcolato) può esserci solo un indice di matrice zero. Preferisco che la barra zero sul grafico coincida con l'indice zero dell'array di buffer dell'indicatore, in modo che i principianti non si confondano.
Ho provato a descrivere il codice, spero sia corretto. Forse potrà aiutare anche qualcun altro, se è corretto.
//+------------------------------------------------------------------+//| 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,// как при первом запуске.
Non mi piace affatto il rollover dell'indicizzazione del buffer. Ecco perché ho deciso di mostrare una variante alternativa dell'indicatore
//+------------------------------------------------------------------+//| 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, a volte chiunque si stanca e commette errori. Non considerarla una critica, ma rates_total - Bars() sarà sempre zero...
In generale, è molto difficile spiegare questo personaggio. Una vittima degli USE...
rates_total èBars() nella frase. Cioè,rates_total è Bars().
Nel codice sottostante
Ha ragione.
Non ho preso l'USE. Allora non esisteva.Ad esempio MA5, media di 5 barre, impostata per visualizzare 100 barre. L'indicatore calcolerà dalla 104a alla 100a barra e inizierà a tracciare la linea dalla 100a barra. Quindi rates_total=5, o 100, o 104?
Quindi il calcolo verrà effettuato sulle ultime 100 barre.
In sostanza rates_total e Bars() sono la stessa cosa, solo cheBars() è una funzione, quindi chiamarla costerà di più in termini di tempo di esecuzione che leggere il valore della variabile rates_total.
Grazie, è molto chiaro.
Secondo lei cosa ho scritto di sbagliato? Giustificatelo, per favore.
Qual è il limite e da dove viene il loop nel mio e nel tuo esempio.
Beh, allora il Buffer0 dovrebbe essere indicizzato come nella serie temporale ArraySetAsSeries(Buffer0,true); altrimenti l'esempio non è chiaro.
In generale, è così. Ritengo che non sia molto corretto calcolare gli indicatori dalla barra zero al passato.
Io li calcolo sempre dal passato al presente. Tracciamo una linea sulla chiusura:
Specificare il numero di barre da contare:
Specificare il numero di barre da contare:
Grazie. È una risposta molto dettagliata!
La risposta è la seguente. Penso che non sia molto corretto calcolare gli indicatori dalla barra zero a ritroso nel tempo.
State confondendo di nuovo i principianti)
Negli indicatori MQL5, finché non si inverte l'indicizzazione, la barra zero è il passato.
P.S. Artem ha di nuovo ragione. Ho usato il termine sbagliato, invece di "barra zero" avrei dovuto scrivere "indice zero".
Grazie. È una risposta molto dettagliata!
Stai confondendo di nuovo i neofiti)
Negli indicatori MQL5, finché non si inverte l'indicizzazione, la barra zero è il passato.
Credo di aver dato una risposta esauriente. Ho allegato due indicatori che sono leggermente diversi. Un nuovo arrivato che ha visto il significato diventerà un normale veterano e darà lui stesso dei suggerimenti. E chi sarà confuso - beh, significa "cosa è necessario?".
Ho tutti i buffer, la loro indicizzazione, distribuiti. E la barra dello zero è sul grafico. Nell'indicatore, nel suo buffer disegnato (e anche in quello calcolato) può esserci solo un indice di matrice zero. Preferisco che la barra zero sul grafico coincida con l'indice zero dell'array di buffer dell'indicatore, in modo che i principianti non si confondano.
Ho provato a descrivere il codice, spero sia corretto. Forse potrà aiutare anche qualcun altro, se è corretto.
Non mi piace affatto il rollover dell'indicizzazione del buffer. Ecco perché ho deciso di mostrare una variante alternativa dell'indicatore