//+------------------------------------------------------------------+//| 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,// как при первом запуске.
//+------------------------------------------------------------------+//| 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,有时候任何人都会累,都会犯错。不要认为这是批评,但rates_total - Bars() 总是零...
一般来说,很难向这个角色解释。USE 的受害者...
句中的 rates_total 是Bars()。即rates_total is Bars()。
在下面的代码中
他说得对。
我没有接受 USE。当时它还不存在。例如 MA5,5 条平均线,设置为显示 100 条。那么指标将从第 104 个条形图计算到第 100 个条形图,并从第 100 个条形图开始画线。那么rates_total=5,还是 100,还是 104?
所以计算将在最后 100 个条形图上进行。
本质上rates_total 和Bars() 是 一样的,只是Bars() 是一个函数,所以调用它比读取rates_total 变量的值花费更多的执行时间。
谢谢,这就很清楚了。
你认为我写错了什么?请说明理由。
在我和你的例子中,循环的极限是什么?
那么Buffer0 就应该像在时间序列ArraySetAsSeries(Buffer0,true) 中那样进行索引;否则示例就不清楚了。
总的来说,是这样的。我认为,从零开始计算过去的指标是不太正确的。
我总是从过去计算到现在。这里,我们在收盘价上画一条线:
指定要计算的条数:
指定要计算的条数:
谢谢。你的回答很详细!
是这样的。我认为从零开始计算指标是不太正确的。
您又在迷惑初学者了)
在MQL5 指标 中,在您反转索引 之前 ,零柱就是过去。
附注:Artem 又说对了。我用错了术语,我应该写 "零指数",而不是 "零柱"。
谢谢您的回答回答得很详细!
你又把新手搞糊涂了)
在MQL5 指标 中,在 您反转索引 之前 ,零条就是过去。
我想我已经给出了详尽的答案。我附上了两个略有不同的指标。看懂了意思的新手会成长为普通的老手,然后他会自己给出提示。谁会感到困惑--嗯,意思是 "这有什么必要?
我已经部署了所有缓冲器及其索引。图表上有零柱。在指标中,其绘制的缓冲区(以及计算的缓冲区)中只能有一个零数组索引。我希望图表上的零点条与指标缓冲区数组的零点索引一致,这样初学者就不会混淆。
我试着描述了代码,希望是正确的。如果正确的话,也许对其他人也有帮助。
我非常不喜欢缓冲区索引的滚动。因此,我决定展示该指标的另一种变体