English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Переход на новые рельсы: пользовательские индикаторы в MQL5

Переход на новые рельсы: пользовательские индикаторы в MQL5

MetaTrader 5Примеры | 23 ноября 2009, 17:57
18 647 10
TheXpert
TheXpert

Введение

Наконец мы получили возможность попробовать в работе новый торговый терминал - MetaTrader 5. Вне сомнения, продукт заслуживает внимания и имеет множество новых возможностей по сравнению со своим предшественником. Важными преимуществами этой платформы среди прочих являются:

  • Существенно доработанный язык, позволяющий теперь программировать, используя принципы ООП, и при этом оставляя богатые возможности для функционального программирования
  • Скорость исполнения кода, которая в разы превышает оную в терминале MetaTrader 4.
  • Существенное увеличение возможностей для отображения необходимой информации.

Я не буду перечислять все новые возможности и особенности нового терминала и языка. Их действительно много, и некоторые новинки вполне достойны освещения в отдельной статье. Вы не увидите здесь кода, написанного по принципам объектно-ориентированного программирования - это слишком серьезная тема для того, чтобы просто быть упомянутой в контексте как дополнительная вкусность для кодописателей.

В этой статье остановимся подробней на индикаторах, их строении, отображении, видах, а также особенностях их написания по сравнению с MQL4.

В принципе, в статье нет материала сложного для восприятия, а кроме того, любой рассмотренный аспект можно проверить непосредственно в коде и в терминале, скачав приложенные файлы.

Надеюсь, ее прочтение будет полезным как начинающим, так и опытным разработчикам, которые, возможно, найдут здесь что-то новое для себя.

 

Общая структура

Общая структура индикатора по сравнению с MQL4 не поменялась. Как и раньше, есть три функции - для инициализации, непосредственно обработки данных и завершения работы индикатора.

Как и раньше, многие параметры можно задавать свойствами (директива #property). БОльшая их часть предназначена именно для индикаторов. Свойства и входные параметры, как и раньше, задаются в глобальном контексте.

В качестве примера разберем реализацию раскраски направления индикатора RSI. Здесь представлена урезанная версия, полная же находится в файле Color_RSI.mq5

Рассмотрим код частями.

//+------------------------------------------------------------------+
//|                                                    Color_RSI.mq5 |
//+------------------------------------------------------------------+
//--- группа информационных свойств.
#property copyright "TheXpert"
#property link      "theforexpert@gmail.com"
#property version   "1.00"
//--- описание индикатора - суммарно не должно превышать 511 символов
//--- с учетом символов перевода строки
#property description "      "
#property description "Демонстрация построения индикатора"
#property description "на примере раскрашивания RSI"

Заданные выше свойства отображаются на информационной панели индикатора (закладка "Common" в свойствах). Вот как это выглядит:

//--- свойства непосредственно индикатора
#property indicator_separate_window // индикатор будет отображен в отдельном подокне
#property indicator_buffers 2       // количество используемых буферов
#property indicator_plots   1       // количество отображаемых буферов
//--- plot 1
#property indicator_color1 clrDarkSalmon, clrDeepSkyBlue // используем 2 цвета
#property indicator_type1  DRAW_COLOR_LINE               // и специальный цветной тип отображения

Эти свойства относятся непосредственно к индикатору. Описание остальных свойств индикатора (и не только) можно посмотреть в справке.

//---- buffers
double Values[];                 // буфер значений
double ValuesPainting[];         // буфер индексов цветов
//--- входные параметры индикатора
input string             _1           = "Параметры для RSI";
input int                RSIPeriod    = 5;
input int                SmoothPeriod = 5;
input ENUM_APPLIED_PRICE AppliedPrice = PRICE_CLOSE;
input string             _2           = "Настройки цветов";
input color              Down         = clrDarkSalmon;
input color              Up           = clrDeepSkyBlue;
//--- переменная, в которой будет содержаться хэндл индикатора
int RSIHandle;

Это входные параметры и глобальные переменные (не путать с глобальными переменными терминала). Входные параметры индикатора задаются идентификатором input.

Теперь в качестве входного параметра можно задавать перечисления, если необходимо, чтобы пользователь мог ввести только допустимые значения параметра.

Параметр AppliedPrice будет отображен выпадающим списком с возможными допустимыми значениями.

Так же (выпадающим списком) будут отображаться любые перечисления, в том числе и пользовательские. Например, следующий параметр

//...
enum DayOfWeek
{
   Понедельник,
   Вторник,
   Среда,
   Четверг,
   Пятница,
   Суббота,
   Воскресенье
};

input DayOfWeek Day;

//...
будет отображен так:

int OnInit()
  {
//--- привязываем индикаторные буферы
//--- Values как буфер для отображения
   SetIndexBuffer(0,Values,INDICATOR_DATA);
//--- ValuesPainting как буфер для хранения цветов
   SetIndexBuffer(1,ValuesPainting,INDICATOR_COLOR_INDEX);
//--- Устанавливаем начало рисования буфера Values
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,RSIPeriod);
//--- Устанавливаем имя индикатора
   IndicatorSetString(INDICATOR_SHORTNAME,"Цветной RSI("+string(RSIPeriod)+")");
//--- Устанавливаем пустое значение для буфера Values
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
//--- Устанавливаем цвета буфера
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,0,Down);
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,1,Up);
//--- Получим хэндл индикатора
   RSIHandle=iRSI(NULL,0,RSIPeriod,AppliedPrice);
//--- Устанавливаем порядок индексации буферов
   ArraySetAsSeries(Values,true);
   ArraySetAsSeries(ValuesPainting,true);
//--- успешное выполнение
   return(0);
  }

Функция инициализации индикатора. Здесь регистрируются буферы, задаются настройки индикатора, которые нельзя задать в свойствах или необходимо задать динамически, а также свойства непосредственно буферов.

Также здесь происходит начальная инициализация данных, в том числе создание хэндлов, необходимых для работы индикаторов.

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- количество баров для расчета
   int toCount=(int)MathMin(rates_total,rates_total-prev_calculated+1);
//--- попытаемся скопировать данные индикатора iRSI
   if(CopyBuffer(RSIHandle,0,0,toCount,Values)==-1)
     {
      Print("Ошибка во время копирования данных, №",GetLastError());
      //--- вернем команду на пересчет значений индикатора
      return(0);
     }
//--- раскрашивание. Да, теперь оно стало таким простым
   for(int i=toCount-2;i>=0;--i)
     {
      //--- раксраска первой линии
      if(Values[i+1]!=EMPTY_VALUE && Values[i]>Values[i+1])
         ValuesPainting[i]=1;
      else
         ValuesPainting[i]=0;
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

Функция расчета данных. Эта функция может быть двух видов. Здесь приведен стандартный вид. Подробней об особенностях немного ниже.

//--- присутствие функции в коде не обязательно
/*
void OnDeinit()
{

}
*/

Функция завершения индикатора. Обычно она необходима, например, для того, чтобы освободить захваченные ресурсы, в частности, файлы. Если такой необходимости нет, определять функцию не обязательно.

 

Две концепции индикаторов

Первая - стандартная, та, к которой мы привыкли в MQL4, правда в несколько видоизмененной форме. Вместо функции Start используется функция OnCalculate. Для стандартной формы она выглядит следующим образом:

int OnCalculate(const int rates_total,      // размер массивов
                const int prev_calculated,  // обработано баров на предыдущем вызове
                const datetime& time[],     // данные для текущего графика и ТФ ...
                const double& open[],
                const double& high[],       
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
  {
   return rates_total;
  }

Дабы уменьшить количество кода, предназначенного для копирования данных, данные графика передаются массивами прямо в параметрах функции.

Кроме того, первым параметром передается количество доступных баров, а вторым - значение, возвращенное при предыдущем вызове этой функции или 0.

0 может быть передан при первом запуске индикатора, а также подкачке новых или пропущенных данных. Этот параметр - замена (альтернатива, аналог, на ваш вкус) не любимой многими функции IndicatorCounted().

Вторая концепция – замена и расширение класса функций MQL4 i<…>OnArray. В примерах терминала есть индикатор такого типа - Custom Moving Average. Этот класс индикаторов предназначен для обработки данных по выбору пользователя, в том числе и пользовательских индикаторов.

Функция обработки для этого типа индикаторов выглядит так:

int OnCalculate (const int rates_total,      // размер массива price[]
                 const int prev_calculated,  // обработано баров на предыдущем вызове
                 const int begin,            // откуда начинаются значимые данные
                 const double& price[]       // массив для расчета
                 )
  {
   return rates_total;
  }
Последним параметром передаются данные, выбранные пользователем для обработки. Естественно, в случае, если вы хотите обработать индикатор с множеством буферов, на обработку будет передан первый буфер индикатора.

First Indicator's Data значит, что индикатор будет применяться к индикатору, который был первым повешен на график для выбранного окна.

Previous Indicator's Data значит, что индикатор будет применяться к индикатору, который был последним повешен на график для выбранного окна.

Из таких индикаторов можно собирать целые стеки. Например, с помощью индикатора Custom Moving Average можно получить трехкратное сглаживание, наложив первый индикатор на необходимые данные, второй на первый, а третий на второй:

 

Многие стандартные индикаторы реализуют именно эту концепцию. Поэтому, когда в подсказке функции вы видите параметр applied_price_or_handle:

то это говорит о том, что индикатор реализован таким образом, что может быть рассчитан на пользовательских данных - хэндл на эти данные должен быть передан параметром applied_price_or_handle.

Таким же образом можно организовать обработку данных прямо в коде индикатора:

  {
   //...
   RSIHandle = iRSI(NULL, 0, RSIPeriod, AppliedPrice);
   SmoothHandle = iMA(NULL, 0, SmoothPeriod, 0, MODE_EMA, RSIHandle);
   //...
  }

Еще одно новое применение данной концепции - возможность написания универсальных сервисных индикаторов. Пример такого индикатора приложен - он называется Direction_Brush.mq5.

На верхнем графике виден результат применения индикатора. Таким образом, раскраска направления выделена в отдельную сущность и вынесена в отдельный индикатор.

Конечно, универсальность их ограничена, т.к. они применимы только для нулевого буфера индикатора. Тем не менее, думаю, для них найдется своя ниша.

А если посмотреть с другой стороны, то при написании пользовательского индикатора, в принципе, стОит учитывать этот момент, т.к. вынос основной информации в нулевой буфер позволит не делать в одном индикаторе функциональный комбайн. Множество вспомогательных действий может быть вынесено и выполнено во внешних сервисных индикаторах. Все, что надо будет сделать - навесить сервисные индикаторы с требуемым функционалом на свой пользовательский.

А диапазон применения не так узок, как могло бы показаться с первого взгляда:

  • раскрашивание особенностей индикатора (вершины, направление, уровни, сегменты и т.д.), в том числе тоновая визуализация;
  • различные сигналы по различным условиям;
  • сбор и отображение статистики – в качестве примера можно привести вывод распределения данных;
  • построение универсальных индикаторов, которые можно рассчитать для одного буфера – как пример – скользящие средние, зигзаг.

Эти возможности показывают далеко не полный список вариантов применения концепции. Думаю, со временем обязательно найдутся другие способы эффективного использования.

 

Доступ к данным

В MQL5 изменены принципы доступа к данным. Работа происходит напрямую с массивами, отсюда существенное ускорение расчетов. Теперь не надо самим создавать массив и для каждого значения в очередной раз вызывать не самую быструю функцию iCustom. Вместо этого можно получить необходимое количество данных, вызвав одну функцию, а затем можно пользоваться непосредственно требуемыми данными, скопированными в локальный заданный массив.

Копирование происходит с помощью системной функции CopyBuffer. В справке есть подробное ее описание.

Максимально количество данных для копирования для индикаторных и статических (с заданным размером) массивов определяется размером массива. Размер динамического массива может быть изменен, если количество копируемых данных превышает его размер.

Кроме этого, есть отдельные функции для доступа к историческим данным:

Функция Описание
CopyBuffer Получает в массив данные указанного буфера от указанного индикатора.
CopyRates Получает в массив исторические данные структуры Rates для указанных символа и периода.
CopyTime Получает в массив исторические данные по времени открытия баров по соответствующим символу и периоду.
CopyOpen Получает в массив исторические данные по цене открытия баров по соответствующим символу и периоду.
CopyHigh Получает в массив исторические данные по максимальной цене баров по соответствующим символу и периоду.
CopyLow Получает в массив исторические данные по минимальной цене баров по соответствующим символу и периоду.
CopyClose Получает в массив исторические данные по цене закрытия баров по соответствующим символу и периоду.
CopyTickVolume Получает в массив исторические данные по тиковым объемам для соответствующих символа и периода.
CopyRealVolume Получает в массив исторические данные по торговым объемам для соответствующих символа и периода.
CopySpread Получает в массив исторические данные по спредам для соответствующих символа и периода.

Подробней про них можно почитать в справке.

Эти данные передаются только в одной форме индикатора, для другой их необходимо получать самим.

Из-за того, что тип массива исторических данных не обязательно double, для их хранения рекомендуется использовать только динамические неиндикаторные буферы соответствующего типа.

Также следует помнить о том еще одна неописанная тонкость заключается в том, что если количество копируемых данных равно 0 (нулю), функция CopyBuffer (как и любая другая функция для копирования данных) сгенерирует ошибку 4003, поэтому количество копируемых данных должно быть не менее 1 (одного) элемента.

 

Буферы индикатора

Количество буферов не ограничено.

Теперь не придется думать, как бы вместить информацию и при этом корректно и эффективно провести промежуточные вычисления, работая над созданием кластерного индикатора.

Правда, при этом не стОит забывать о том, что хранение данных буферов требует памяти. Поэтому, поставив в терминале глубину отображаемой истории 1,000,000 баров и повесив на минутный график «толстый» кластерный индикатор, не удивляйтесь, когда терминал скушает гиг памяти.

Сущность буфера также претерпела некоторые изменения. Количество используемых буферов задается в свойстве индикатора:

#property indicator_buffers 2       // количество используемых буферов

Число в свойстве должно строго соответствовать общему количеству буферов.

Количество рисуемых буферов задается в свойстве

#property indicator_plots 1         // количество отображаемых буферов

Здесь немного тоньше. Большинство стилей отображения требуют один буфер INDICATOR_DATA для отображения. Однако есть стили, которые требуют сразу несколько индикаторных буферов для отображения.

Речь идет о таких стилях отображения как:

  • DRAW_HISTOGRAM2 - требуется два индикаторных буфера для отображения (HistogramSample.mq5)

  • DRAW_FILLING - требуется два индикаторных буфера для отображения (CrossMa.mq5)

  • DRAW_CANDLES - требуется четыре буфера (CandleSample.mq5)

  • DRAW_BARS - требуется четыре буфера (BarsSample.mq5)

Для всех вышеперечисленных типов, кроме DRAW_FILLING (он не бывает не цветным), есть цветные аналоги.

Все индикаторные буферы теперь делятся на 3 типа:

  • INDICATOR_DATA - буферы, данные которых выводятся на график. Т.е. эти буферы предназначены для рисования и для обращения с помощью iCustom. Они всегда должны быть зарегистрированы в первую очередь. Если их порядок будет произвольным (неверным), все успешно скомпилируется и будет рисоваться при применении к графику, однако, скорей всего, неправильно.
  • INDICATOR_COLOR_INDEX - буферы для хранения цветов. Они необходимы для хранения индексов цветов буферов типа INDICATOR_DATA, имеющих один из специальных цветных типов (#property indicator_typeN). Такой буфер (назовем его цветовой) должен регистрироваться сразу после буфера, для которого он хранит цвета.
  • INDICATOR_CALCULATIONS - буферы этого типа предназначены для хранения результатов вспомогательных вычислений. На графике они не отображаются.
int OnInit()
  {
   //...
   SetIndexBuffer(0, V2, INDICATOR_DATA);
   SetIndexBuffer(1, V2C,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2, V4, INDICATOR_DATA);
   SetIndexBuffer(3, V4C,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(4, V1, INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, V3, INDICATOR_CALCULATIONS);
   //...
   return 0;
  }

Есть также некоторые нюансы при обращении к индикаторам через iCustom.

  • Буферы для отображения (те, которые отображаются на графике) доступны для чтения. Номер буфера должен совпадать с тем, под которым буфер был зарегистрирован.
  • Буферы для хранения цветов могут быть доступны для чтения, но не всегда. Например, в коде выше буфер V2C можно прочитать и получить необходимые значения, но буфер V4C недоступен.
  • Буферы для промежуточных вычислений недоступны, как это было в MQL4. Если вы хотите гарантированно иметь внешний доступ к информации буфера, объявляйте его как INDICATOR_DATA.

В случае, если вы хотите обратиться к недоступному буферу, будет сгенерирована ошибка 4806 ("Запрошенные данные не найдены").

Остановимся подробней на цветных буферах.

Если в MQL4 для каждого цвета необходимо было заводить отдельный буфер, теперь, используя цветные стили, можно задавать одному буферу до 63 цветов. В некоторых случаях это позволит сэкономить количество используемых буферов, а значит, сэкономить количество используемой памяти. Также это открывает новые возможности при написании индикаторов, в частности, применение тоновой визуализации.

Кроме того, это нововведение в некоторых случаях намного упрощает логику применения нескольких цветов по сравнению с MQL4. Самый наглядный пример - разделение цветами направлений.

В MQL4 в самом экономном (из правильных) варианте исполнения для реализации задачи требовалось 3 (три) буфера и не самая тривиальная логика. Сейчас это проще простого. Вот выкладки из кода, полностью код можно посмотреть в файле Color_RSI.mq5.

#property indicator_color1 clrDarkSalmon, clrDeepSkyBlue // используем 2 цвета
#property indicator_type1  DRAW_COLOR_LINE               // и специальный цветной тип отображения
//---- buffers
double Values[];                 // буфер значений
double ValuesPainting[];         // буфер индексов цветов
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- привязываем индикаторные буферы
//--- Values как буфер для отображения
   SetIndexBuffer(0,Values,INDICATOR_DATA);
//--- ValuesPainting как буфер для хранения цветов
   SetIndexBuffer(1,ValuesPainting,INDICATOR_COLOR_INDEX);
//... 
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(/*...*/)
  {
//--- количество баров для расчета
   int toCount=(int)MathMin(rates_total,rates_total-prev_calculated+1);
//--- попытаемся скопировать данные индикатора iRSI
   if(CopyBuffer(RSIHandle,0,0,toCount,Values)==-1)
     {
      Print("Ошибка во время копирования данных, №",GetLastError());
      //--- вернем команду на пересчет значений индикатора
      return(0);
     }
//--- раскрашивание. Да, теперь оно стало таким простым
   for(int i=toCount-2;i>=0;--i)
     {
      if(Values[i+1]!=EMPTY_VALUE && Values[i]>Values[i+1])
         ValuesPainting[i]=1;
      else
         ValuesPainting[i]=0;
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

Еще немного кода, и получается:

При использовании цветных типов отображения есть некоторые нюансы, о которых следует помнить.

Для цветных буферов при динамической схеме задания цветов можно задать максимум то количество цветов, которое присутствует в свойстве indicator_colorN. Например, в случае

#property indicator_color1 clrDarkSalmon, clrDeepSkyBlue // используем 2 цвета

цветовая схема буфера будет содержать максимум два цвета, даже если динамически (c помощью функции PlotIndexSetInteger) установить большее количество цветов.

Поэтому необходимое количество цветов должны быть записаны в одной строчке – строчке задания свойства. Потом их можно поменять динамически. Самый короткий по записи цвет, который я нашел - красный (clrRed). Впрочем, всегда можно сделать так:

Вместо

#property indicator_color1  clrRed, clrRed, clrRed, clrRed, clrRed, clrRed, clrRed, clrRed, //…

Можно сделать так:

#define C clrRed
#property indicator_color1  C, C, C, C, C, C, C, C, C, C, C, C, C, C, //…

Максимальное количество цветов для одного буфера - 63. При количестве цветов больше максимального (при задании свойства indicator_colorN) буфер отображаться не будет.

Вот пример реализации тоновой визуализации, использовано максимальное количество цветов:

В целом, возможностей по отображению значительно прибавилось, что не может не радовать.

 

Массивы

При непосредственном обращении к данным массивов по индексам необходимо иметь в виду порядок данных - свойство AsSeries. Не у всех массивов можно его установить.

Флаг не может быть установлен у многомерных и у статических массивов. У массивов, передаваемых в функцию OnCalculate, флаг устанавливать можно.

Выполнение копирования функцией CopyBuffer от свойства AsSeries не зависит, однако поведение функции CopyBuffer для разных буферов различается.

Для индикаторных буферов данные необходимо копировать на всю глубину доступной истории. Это нужно помнить. Для динамических и статических (с заданным размером) массивов копирование происходит от настоящего к прошлому.

Кроме того, для динамических(не индикаторных) буферов функция CopyBuffer изменяет размер на достаточный для копирования, если прежнего не хватает.

Статические массивы для копирования данных использовать не рекомендуется.

Вообще, советую всегда проверять, как вы копируете данные и как обращаетесь. Самый простой и безопасный спосoб:

  • установить явно всем используемым для хранения исторических данных буферам одинаковое значение свойства AsSeries.
  • учитывать особенности копирования для разных буферов.
  • всегда проверять значение переменной _LastError.

Дополнительно очень советую внимательно проштудировать справку по CopyBuffer и функциям, связанным с AsSeries.

 

IndicatorCounted

Теперь споры о необходимости функции IndicatorCounted канут в лету, т.к. это значение задается непосредственно нами как возвращенное значение предыдущего вызова функции.

int OnCalculate(const int rates_total,      // размер массивов
                const int prev_calculated,  // обработано баров на предыдущем вызове
                //...)
  {
   return rates_total;
  }

Чаще всего достаточно вернуть значение параметра rates_total, которое содержит количество баров при текущем вызове функции.

Правда, если с момента последнего вызова функции OnCalculate() ценовые данные были изменены (подкачана более глубокая история или были заполнены пропуски истории), то значение входного параметра prev_calculated будет установлено в нулевое значение самим терминалом.

Также, если функция OnCalculate возвращает нулевое значение, то в окне DataWindow клиентского терминала значения индикатора не показываются. Поэтому, если вы хотите видеть индикатор во время подкачки истории или после обрыва связи, и в то же время пересчитать его полностью - возвращайте значение 1 вместо 0.

Также появилась полезная возможность - узнать, сколько баров рассчитано для индикатора. Полезно больше для советников, которые внутри производят вычисления на большом отрезке данных индикатора - BarsCalculated (подробней про функцию можно почитать в справке).

У этой функции есть еще одно несомненно полезное применение. Дело в том, что между созданием хэндла на индикатор и использованием его для расчетов может понадобиться некоторое время в том случае, если индикатор не загружен. Это время необходимо для инициализации и первичного просчета индикатора. Оно зависит от скорости расчетов и наполнения индикатора.

Во время этого промежутка времени при выполнении функции CopyBuffer генерируется ошибка 4806 - "Запрошенные данные не найдены".

Для того, чтобы узнать, доступны ли данные индикатора для копирования, можно использовать функцию BarsCalculated:

//--- количество баров для расчета
   int toCount=rates_total-(int)MathMax(prev_calculated-1,0);
//--- попытаемся скопировать данные индикатора iWPR
   int copied=CopyBuffer(WPRHandle,0,0,toCount,Values);
   int err=GetLastError();
//--- проверка результата копирования
   if(copied==-1)
     {
      //--- если номер ошибки 4806, то данные просто не успели подгрузиться
      if(err==4806)
        {
         //--- ждем, пока данные загрузятся
         for(int i=0;i<1000;++i)
            if(BarsCalculated(WPRHandle)>0)
               break;
         //--- попытаемся скопировать данные индикатора iWPR еще раз
         copied=CopyBuffer(WPRHandle,0,0,rates_total,Values);
         err=GetLastError();
        }
     }
//--- проверка результата копирования
   if(copied==-1)
     {
      Print("Error when trying to get WPR values, last error is ",err," bars ",rates_total);
      return(0);
     }
//...

Также есть очень полезная аналогичная функция для исторических данных - SeriesInfoInteger.

Свойств с помощью нее можно узнать немного - всего 4, однако все они могут понадобиться для корректной обработки данных.

Подробней про функцию можно почитать в справке.

 

Заключение

В заключение хочется сказать, что в статье описаны далеко не все нюансы и особенности. Но, надеюсь, основные аспекты здесь присутствуют.

Хотелось бы, чтобы статья стала чем-то вроде пособия, куда можно всегда заглянуть, поискав нужный тонкий момент в работе, и найти информацию по нему. Поэтому, если вы заметите неточность или ошибку в статье, или найдется нечто важное, что я упустил — большая просьба, сообщите мне об этом, я постараюсь исправить ошибки или добавить упущенное максимально оперативно.

Планирую отмечать изменения, а также отмечать несущественные дополнения в комментах, кроме того, надеюсь, в отзывах также будет появляться полезная информация.

Поэтому, наверное, комментарии тоже стОит внимательно прочесть.

 

Приложения

  • Color.mqh - это include файл, его необходимо положить в папку MQL5/Include. Файл необходим для работы индикатора Toned_WPR.
  • Color.mq5 - это библиотека, ее необходимо положить в папку MQL5/Libraries. Файл необходим для работы индикатора Toned_WPR.

Все остальные файлы являются индикаторами.

 

Благодарности

В очередной раз хочется выразить свою благодарность Рустамову Виктору Хабибулаевичу (granit77) за то, что нашел время просмотреть черновые варианты статьи, выразить свое мнение и исправить ошибки и неточности.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/5

Прикрепленные файлы |
crossma.mq5 (2.52 KB)
candlesample.mq5 (2.32 KB)
barssample.mq5 (2.32 KB)
color.mq5 (4.3 KB)
color.mqh (1 KB)
toned_wpr.mq5 (4.77 KB)
color_rsi.mq5 (5.72 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (10)
Prival
Prival | 1 мая 2010 в 11:45

только начал изучать. скачал положил в папку D:\MetaTrader 5\MQL5\Indicators 

откомпелировал, накинул на график. некоторые индикаторы ничего не показывают  ((. 

Так и должно быть ? или я что то не то сделал. 

не показывают индикаторы CrossMa.mq5, Toned_WPR.mq5 и HistogramSample.mq5

остальные работают 

Vasily
Vasily | 1 мая 2010 в 13:01
Rosh:

Что означает - "неправильно"?  Надо ведь приводить конкретные примеры вместо общих заявлений "все плохо".

Попробуйте запустить пример из раздела CopyHigh():

Вот что получилось у меня, все показывает верно.



весь массив копировать данных???

помоему очень не экономично можно же 1 элемент скопировать

TheXpert
TheXpert | 3 мая 2010 в 13:02

Prival:

Так и должно быть ? или я что то не то сделал.

Привет, Сергей. Статья писалась с пылу с жару, для самых первых публичных билдов. С тех пор очень многое поменялось, возможно, некоторые индикаторы требуют доработки.

Пересмотрю, как только будет время.

Yura Fomin
Yura Fomin | 12 сент. 2020 в 17:35

Здравствуйте.. Почитал .. посмотрел..

В RSI_Color в написании есть лишние строки:

Tак как цвета восходящих и нисходящих линий графика описаны в блоке input color, то, как говорится в справочнике: " Изменять значение переменной с модификатором input внутри mql5-программы нельзя, переменные доступны только для чтения. значения input-переменных может менять только пользователь из окна свойств программы."

Зафлешил в комменты  функцию установки цветов // PlotIndexSetInteger(1,PLOT_LINE_COLOR,0,Down);   // PlotIndexSetInteger(1,PLOT_LINE_COLOR,1,Up); Результат, действительно, не изменился

Andrei Trukhanovich
Andrei Trukhanovich | 12 сент. 2020 в 23:05
Yura Fomin:

Здравствуйте.. Почитал .. посмотрел..

Здравствуйте. То, что коды из статьи, написанной еще во времена, когда МТ5 был в публичной бете, вообще работают, говорит о том, что система индикаторов в МТ5 была сразу целостной и тщательно продуманной.

Но с тех пор многие вещи поменялись, некоторые кардинально. Если вам понятны общие принципы, более конкретную актуальную информацию лучше брать из справки и свежих статей.

Спасибо за уточнения (хотя честно говоря не понял как приведенный код нарушает утверждение из справки), но думаю статья давно сослужила свою службу, поэтому исправлений не планируется

Используем нейронные сети в MetaTrader Используем нейронные сети в MetaTrader
В статье показано как применять нейронные сети в программах на MQL, используя свободно распространяемую библиотеку FANN.На примере стратегии с использованием индикатора MACD построен эксперт, использующий нейросетевую фильтрацию сделок, которая привела к улучшению характеристик торговой системы.
Автоматическое создание документации к программам на MQL5 Автоматическое создание документации к программам на MQL5
Большинство Java программистов знакомы с автоматическим созданием документации, которая может быть создана при помощи программы JavaDocs. В мире C++ также есть несколько автоматических генераторов документации, одними из лидеров являются программы Microsoft's SandCastle и Doxygen. В статье описано, как можно использовать программу Doxygen для создания структурированных файлов справки HTML для программ, написанных на MQL5. Результаты данной работы убедили меня использовать Doxygen (или похожие программы) в будущем для создания документации к любому моему коду на MQL5, это значительно облегчает его понимание и использование.
Вот мы и получили долгожданные MetaTrader 5 и MQL5 Вот мы и получили долгожданные MetaTrader 5 и MQL5
Это очень краткий обзор MetaTrader 5. Я не могу описать все новшества системы за столь короткий период времени - тестирование стартовало 09-09-2009. Это символическая дата, и я уверен, что это будет счастливым числом. Всего несколько дней у меня на руках бета-версия терминала MetaTrader 5 и MQL5. Я не успел опробовать все, что в нем есть нового, но то, что есть, уже впечатляет.
Alert и Comment для внешних индикаторов. Мультивалютный анализ посредством внешнего сканирования Alert и Comment для внешних индикаторов. Мультивалютный анализ посредством внешнего сканирования
Алерт для мультивалютного и мультитаймфреймного анализа внешних индикаторов. В статье рассматривается способ получения информации о событиях происходящих во внешних индикаторах без присоединения их на график и без открытия самих графиков. Назовем это внешним сканированием.