В настоящее время многие трейдеры применяют пользовательские
индикаторы. Кратко напомним, что все индикаторы для MetaTrader
4 делятся на две группы - технические и пользовательские. Технический индикатор
- это часть торговой платформы, недоступная для внесения изменений в программный
код. Пользовательский индикатор - это прикладная программа, написанная на
MQL 4 по собственному алгоритму пользователя. В MQL 4 предусмотрено два варианта отображения индикаторных линий: в
основном окне финансового инструмента и в подокне. В ряде случаев этих
возможностей оказывается достаточно. В других случаях имеется потребность
отображать данные индикатора одновременно в двух окнах - и в подокне, и в окне
ценового графика.
Здесь мы рассмотрим способ построения пользовательского индикатора,
отображаемого в подокне, который выводит часть данных в основное окно.
Задача.
Имеется пользовательский индикатор ADX
Smoothed_1.mq4. В индикаторе выполняется расчёт индикаторного
массива ADXFinal[] (зелёная линия).
Требуется отобразить цветную метку "+" под баром, для
которого выполняются следующие условия:
- значение ADXFinal[] больше 25;
- значение ADXFinal[] больше, чем значение ADXFinal[]
на предыдущем баре.
Представленный индикатор содержит простые расчёты. В блоке 1-2 указаны
внешние переменные, открыты индикаторные массивы и заданы цвета для индикаторных
линий. В строке
#property indicator_separate_window
предписано, что индикатор выводится в подокно.
В специальной функции init() (блок 2-3) указано
соответствие индикаторного массива номеру индикаторного буфера, а также стили
отображения и названия индикаторных линий. В функции start()
(блок 3-4) выполняется расчёт индикаторных массивов
DiPlusFinal[], DiMinusFinal[] и ADXFinal[]. В виду
отсутствия необходимости, в функции deinit() никакой
программный код не указан.
Итак, индикатор выводится в отдельное окно. Единственным способом решения
поставленной задачи является использование графических объектов. Здесь важно
подчеркнуть, что пользовательский индикатор - это прикладная программа, в
которой можно выполнять необходимые вычисления, в том числе, осуществлять
управление графическими объектами. То, что в головной части индикатора есть
указание на вывод индикаторных линий в подокно, вовсе не означает, что это
указание ограничивает область вывода графических объектов. Обратите внимание,
также, что среди параметров функции
ObjectCreate() есть параметр
window, который определяет номер окна для отображения создаваемого
объекта.
Создание объекта с указанным именем, тип и начальные координаты в
указанном подокне графика.
Таким образом, программист свободен в выборе типов и координат объектов и
окна, в котором объекты будут отображены. Это значит, что для отображения
заданных объектов достаточно вычислить в программе их координаты. Вместе с тем,
использование графических объектов в индикаторах имеет свою специфику.
Главная особенность индикаторов заключается в специфическом методе пересчёта
индикаторных массивов. Напомним, что при переключении таймфреймов, а также при
удалении индикатора из окна финансового инструмента отображением индикаторных
линий управляет клиентский терминал MetaTrader 4. Это
свойство клиентского терминала проявляется независимо от воли программиста (и
зачастую безотчётно для программиста), но применительно лишь к индикаторным
линиям. Однако, это свойство никак не связано графическими
объектами (ни с фактом их отображения, ни с их координатами). Поэтому управление
объектами полностью зависит от программного кода индикатора.
Чтобы разобраться в этом подробнее, рассмотрим код индикатора, который
полностью решает поставленную задачу.
//жжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжж 0 жж
// ADX_Smoothed_2.mq4
// Изменения: Сергей Ковалёв, sk@autograf.dp.ua, ICQ 64015987, http://autograf.dp.ua
//жжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжж 1 жж
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_color1 SlateBlue
#property indicator_color2 FireBrick
#property indicator_color3 DarkGreen
#property indicator_level1 25
//---- input parameters
extern int per = 14;
extern double alpha1 = 0.25;
extern double alpha2 = 0.33;
extern int PriceType = 0;
extern int Qnt_Bar = 300;
extern color Color_Obj = Red;
//---- buffers
double DiPlusFinal[];
double DiMinusFinal[];
double ADXFinal[];
double DIPlusLead[];
double DIMinusLead[];
double ADXLead[];
//жжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжж 2 жж
int init()
{
//---- indicators
IndicatorBuffers(6);
SetIndexStyle(0, DRAW_LINE);
SetIndexBuffer(0, DiPlusFinal);
SetIndexLabel(0, "Di Plus");
//----
SetIndexStyle(1, DRAW_LINE);
SetIndexBuffer(1, DiMinusFinal);
SetIndexLabel(1, "Di Minus");
//----
SetIndexStyle(2, DRAW_LINE);
SetIndexBuffer(2, ADXFinal);
SetIndexLabel(2, "ADX");
//----
SetIndexBuffer(3, DIPlusLead);
SetIndexBuffer(4, DIMinusLead);
SetIndexBuffer(5, ADXLead);
//----
IndicatorDigits(2);
IndicatorShortName("ADX(" + per + ")smothed_2");
//----
return(0);
}
//жжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжж 3 жж
int start()
{
int counted_bars = IndicatorCounted();
int i, k, limit;
double DIPlus, DIMinus, ADX, DIPlus1, DIMinus1, ADX1;
//----
if(counted_bars == 0)
limit = Bars - per - 1;
if(counted_bars > 0)
limit = Bars - counted_bars;
for(i = limit; i >= 0; i--)
{
DIPlus = iADX(NULL, 0, per, PriceType, MODE_PLUSDI, i);
DIMinus = iADX(NULL, 0, per, PriceType, MODE_MINUSDI, i);
ADX = iADX(NULL, 0, per, PriceType, MODE_MAIN, i);
DIPlus1 = iADX(NULL, 0, per, PriceType, MODE_PLUSDI, i + 1);
DIMinus1 = iADX(NULL, 0, per, PriceType, MODE_MINUSDI, i + 1);
ADX1 = iADX(NULL, 0, per, PriceType, MODE_MAIN, i + 1);
//----
DIPlusLead[i] = 2*DIPlus + (alpha1 - 2) * DIPlus1 + (1 - alpha1)*DIPlusLead[i+1];
DIMinusLead[i]= 2*DIMinus + (alpha1 - 2) * DIMinus1+(1 - alpha1)*DIMinusLead[i+1];
ADXLead[i] = 2*ADX + (alpha1 - 2) * ADX1 + (1 - alpha1) * ADXLead[i+1];
DiPlusFinal[i] = alpha2*DIPlusLead[i] + (1 - alpha2) * DiPlusFinal[i+1];
DiMinusFinal[i] = alpha2*DIMinusLead[i] + (1 - alpha2) * DiMinusFinal[i+1];
ADXFinal[i] = alpha2*ADXLead[i] + (1 - alpha2) * ADXFinal[i+1];
//--------------------------------------------------------------------------------- 31 --
if (i < Qnt_Bar && ADXFinal[i] > ADXFinal[i+1] && ADXFinal[i] > 25)
{
string Obj_Name = "ADX_ind_" + Time[i];
if (ObjectFind(Obj_Name) == -1)
ObjectCreate (Obj_Name, OBJ_TEXT, 0,0,0); // Создаём объект
ObjectSet (Obj_Name, OBJPROP_TIME1, Time[i]); // Координата Х
ObjectSet (Obj_Name, OBJPROP_PRICE1, Low[i]); // Координата Y
ObjectSetText(Obj_Name,"+",8,"Arial Black",Color_Obj);// Текстовое описание
WindowRedraw(); // Обновление отображения
}
//--------------------------------------------------------------------------------- 32 --
}
return(0);
}
//жжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжж 4 жж
int deinit()
{
int Qnt_Obj_Del = 0; // Пока нет объектов к удалению
int Kol_Objects = ObjectsTotal(); // Общее количество объектов
string Mas_Name_Del[1]; // Объявление массива
ArrayResize(Mas_Name_Del, Kol_Objects); // Устанавливаем размер массива
//--------------------------------------------------------------------------------- 41 --
for (int k=0; k < Kol_Objects; k++) // По количеству объектов
{
string Obj_Name = ObjectName(k); // Запрашиваем имя объекта
string Bebin = StringSubstr(Obj_Name,0,8);// Извлекаем первые 8 символов из строки
if (Bebin == "ADX_ind_") // Если найден объект, имя которого ..
{ // .. начинается с искомой подстроки
Qnt_Obj_Del=Qnt_Obj_Del+1; // Количество имён объектов к удалению
Mas_Name_Del[Qnt_Obj_Del]=Obj_Name; // Накапливаем массивчик
}
}
//--------------------------------------------------------------------------------- 42 --
for (int i=1; i<=Qnt_Obj_Del; i++) // По именам объектов в массиве
ObjectDelete(Mas_Name_Del[i]); // Удаляем
WindowRedraw(); // Для моментального отображения
return;
}
//жжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжжж 5 жж
Индикатор ADX_Smoothed_2.mq4 отличается от прототипа наличием
блока 31-32 в функции start(), программным кодом в
функции deinit() (блок 4-5), а также наличием внешних
переменных Qnt_Bar (количество баров для отображения меток) и Color_Obj (цвет
меток).
В блоке 31-32 указан код для расчёта координат и создания объектов с
заданным именем. Тело оператора if() исполняется в том
случае, если выполняется следующее условие:
if (i < Qnt_Bar && ADXFinal[i] > ADXFinal[i+1] && ADXFinal[i] > 25)
Выражение i < Qnt_Bar
ограничивает количество отображаемых объектов. Это вычисление включено в
заголовок для того, чтобы повысить быстродействие индикатора при переключении
таймфреймов на большой истории. Пользователь может самостоятельно настроить
количество объектов на исторических барах. В практической работе для
Qnt_Bar может быть установлено значение около 500 баров. Этого вполне
достаточно, чтобы покрыть все бары в пределах окна. При необходимости, для
исследований, количество исторических баров, на которых отображаются объекты,
может быть увеличено до любой желаемой величины.
Другими условиями исполнения тела оператора if()
являются условия задачи, а именно, имеется повышающий наклон индикаторной линии
выше уровня 25. Если условие выполняется, то вычисляется имя графического
объекта, отображаемого на данном ( i-том) баре. На
одном баре должен быть отображён только один объект, поэтому имя объекта состоит
из постоянной части (для всех объектов этого индикатора) и сочетания цифр,
поставленных в соответствие времени открытия бара. Вычисление имени объекта
делать необходимо, т.к. на одном баре, как правило, случается множество тиков. А
поскольку функция start() исполняется на каждом тике,
то при другом методе именования объектов (например, на каждом тике), могли бы
устанавливаться лишние объекты.
Далее, в блоке 31-32, выполняются простые вычисления: если объекта с таким
названием нет, то он устанавливается в вычисленных координатах и с заданным
стилем. Координатами объектов являются: по времени - время открытия бара, по
цене - Low бара. Обратите внимание,
Low бара может изменяться с течением времени. Программный код написан
так, что вычисления координат выполняются на каждом тике. Поэтому, если цена
Low в процессе развития бара изменяется, то
будет выполнена коррекция координаты объекта по цене:
ObjectSet(Obj_Name, OBJPROP_TIME1, Time[i]); // Координата Х
Таким образом, в результате первого исполнения функции
start() все графические объекты, соответствующие условию задачи, будут
установлены под вычисленными барами. При всех последующих исполнениях функции
strat() будут устанавливаться (и, при
необходимости, корректироваться по цене) объекты только на нулевом баре.
Блок 4-5. В представленном индикаторе имеется
также код для удаления графических объектов, установленных индикатором. Функция
deinit() вызывается для исполнения клиентским
терминалом при переключении таймфреймов и при удалении индикатора из окна
финансового инструмента. Необходимость удаления объектов очевидна: в другом
таймфрейме свечной рисунок будет иным, а при удалении индикатора объекты нужно
удалить, чтобы они не накапливались в окне. В отличие от индикаторных линий,
управляемых терминалом, графические объекты необходимо удалить программно.
Наиболее распространённой ошибкой при решении задачи
удаления объектов является использование в deinit()
стандартной функции ObjectsDeleteAll(), предназначенной для удаления всех
объектов. Такое решение может быть оправдано только в случае отладки программ с
целью экономии времени. Для практической работы это решение не приемлемо, т.к.
при исполнении этой функции будут удалены и все полезные объекты, которые
пользователь установил вручную (например, линии поддержки, технические
инструменты и пр.).
В данном случае требуется персонально удалить только те
объекты, которые установлены в результате исполнения программы (индикатора). С
этой целью в программе вычисляются имена "своих" объектов. Это легко выполнить,
т.к. все эти имена содержат одинаковую составляющую часть -
"ADX_ind_".
Общая технология удаления объектов предполагает
следующие этапы. Сначала необходимо вычислить общее количество объектов и
организовать строковый одномерный массив, в котором могли бы поместиться имена
всех объектов. После этого необходимо опросить все объекты и запомнить в этот массив
имена, отобранные по названию. Последним этапом является собственно
удаление объектов.
В блоке 4-41 вычисляется общее количество объектов и
открыт строковый массив Mas_Name_Del[].
По правилам MQL 4 размер массива может быть задан
только константой, поэтому в момент открытия объявляется массив единичной длины.
В следующей строке размер массива устанавливается равным общему количеству
объектов:
stringMas_Name_Del[1];// Объявление массива
ArrayResize(Mas_Name_Del, Kol_Objects); // Устанавливаем размер массива
В блоке 41-42 выполняется запись "наших" имён объектов
в массив Mas_Name_Del[].
Отбор имён выполняется на основании факта содержания в имени объекта заданной
подстроки
"ADX_ind_".
Обратите внимание, вычисления выполняются в отдельном цикле, удаление объектов в
этом цикле не выполняется. Это связано с тем, что в случае удаления одного
объекта все другие объекты будут автоматически переиндексированы терминалом.
Поэтому, в случае удаления объектов в том же цикле, будет происходить
автоматическое "проскакивание" нечётных объектов, т.е. каждый второй объект не
будет подвергнут анализу. В данном же случае накапливание имён объектов,
подлежащих удалению, выполняется корректно.
В блоке 42-5 выполняется собственно удаление объектов,
ранее созданных индикатором. Удаление объектов также выполняется в цикле, общее
количество итераций равно количеству объектов, содержащих заданную подстроку в
названии, т.е. количеству "наших" объектов. К моменту завершения исполнения
функции deinit() в окне ценового графика не остаётся
ни одного объекта, установленного индикатором.
Если функция deinit()
выполняется в результате удаления объекта из окна финансового инструмента, то на
этом исполнение программы в целом закончено - индикатор удалён, собственные
объекты удалены. Если же deinit() исполняется в
результате переключения пользователем таймфрейма, то программа начнёт своё
исполнение сначала. То есть, при первом исполнении start()
в новом таймфрейме объекты будут установлены так же, как и при самом первом
запуске индикатора в окне финансового инструмента.
Пользовательский индикатор, выводимый в подокно и устанавливающий графические
объекты в окне ценового графика
Комментарии
Отправить комментарий