//+------------------------------------------------------------------+//| 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);
}
//+------------------------------------------------------------------+
アルチョム、誰でも疲れてミスをすることはある。批判とは思わないでほしいが、rates_total - Bars()は常にゼロに なる...。
一般的に、このキャラクターに説明するのは非常に難しい。USEの犠牲者...
文中のrates_totalはBars() である。つまり、rates_totalはBars()である。
以下のコードでは
彼は正しい。
私はUSEを取っていない。当時は存在しなかったのだ。例えば、MA5(5本の平均)は、100 本のバーを表示するように設定します。この場合、インジケータは104本目から100本目までを計算し、100本目から線を引き始めます。すると、rates_total=5、または100、104?
つまり、計算は最後の100本のバーで行われます。
要するに、rates_totalとBars() は同じものである。ただ、Bars() は 関数であるため、これを呼び出すと、rates_total 変数の値を 読み込むよりも実行時間がかかる。
ありがとうございます。
私が間違ったことを書いたと思いますか?正当化してください。
私の例とあなたの例では、ループはどこから来るのですか?
Well, then Buffer0 should be indexed as in the time seriesArraySetAsSeries(Buffer0,true); otherwise the example is not clear.
一般的にはこんな感じです。指標をゼロバーから過去に向かって計算するのは、あまり正しくないと思います。
私はいつも過去から現在までを計算します。ここで、Closeに線を引いてみましょう:
カウントする小節数を指定する:
カウントする小節数を指定する:
ありがとうございます。素晴らしい詳細な回答だ!
こんな感じです。ゼロバーから過去にさかのぼって指標を計算するのはあまり正しくないと思います。
また初心者を混乱させていますね)
MQL5の インジケーターでは、インデックスを逆に するまでは 、ゼロバーが過去になります。
追伸:Artemの言う通りです。ゼロバー」ではなく「ゼロインデックス」と書くべきでした。
ありがとう。素晴らしい詳細な回答だ!
また初心者を混乱させている)
MQL5の インジケーターでは、インデックスを逆に するまでは 、ゼロバーが過去になります。
網羅的にお答えしたつもりです。少し違う2つのインジケーターを添付しました。その意味を知った新参者は、普通の古参者に成長し、そして自らヒントを出すだろう。そして、誰が迷うのか--まあ、それは「何が必要なのか」ということだ。
私はすべてのバッファとそのインデックスを配置している。ゼロ・バーはチャート上にある。インジケータでは、描画されたバッファには(そして計算されたバッファにも)ゼロの配列インデックスしかありません。初心者が混乱しないように、チャート上のゼロ・バーとインジケータのバッファ配列のゼロ・インデックスが一致するようにしたいのです。
コードが正しいことを祈りつつ、コードを書いてみた。もしそれが正しければ、他の誰かの助けになるかもしれない。
私はバッファ・インデックスのロールオーバーが本当に嫌いだ。そのため、このインジケータの別のバリエーションを紹介することにした。