English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Индикатор от индикатора в MQL5

Индикатор от индикатора в MQL5

MetaTrader 5Примеры | 8 февраля 2010, 09:32
10 378 26
MetaQuotes
MetaQuotes

Введение

Мы рассмотрим, в качестве задачи, как улучшить индикатор для тех случаев, когда он накладывается на значения другого индикатора. В этой статье мы продолжим работу с индикатором True Strength Index (TSI), который был рассмотрен и создан в предыдущей статье "Как написать индикатор в MQL5".

Пользовательский индикатор на основе значений другого индикатора

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

Сделайте простой эксперимент: набросьте на график встроенный индикатор RSI со стандартными настройками и затем перетащите мышкой на окошко индикатора RSI пользовательский индикатор True_Strength_Index_ver2.mq5. В появившемся окошке на вкладке "Parameters" укажите, что рассчитываться индикатор будет на данных предыдущего индикатора (RSI(14)).

Полученный результат будет сильно отличаться от того, что ожидалось увидеть. В окне индикатора RSI не появилось дополнительной линии индикатора TSI, а если посмотреть значения индикатора в окне DataWindow, то значения будут также непонятны.

Несмотря на то, что значения индикатора RSI есть почти на всем протяжении истории, значения индикатора TSI, построенного на нем, либо совсем отсутствуют (в начале), либо всегда равны -100.

Причина такого поведения кроется в том, что в расчетах внутри функции OnCalculate() нашего индикатора True_Strength_Index_ver2.mq5 нигде не используется значение параметра begin. А ведь именно этот параметр указывает количество пустых начальных значений входного параметра price[], которые нельзя использовать при расчетах значений индикатора. Напомним определение первой формы вызова функции Oncalculate().

int OnCalculate (const int rates_total,      // размер массива price[]
                 const int prev_calculated,  // обработано баров на предыдущем вызове
                 const int begin,            // индекс с которого начинаются значимые данные в массиве price[]
                 const double& price[]       // массив для расчета
   );

Пока мы применяли индикатор на ценовых данных, указывая одну из ценовых констант, параметр begin был равен нулю, так как для каждого бара существует указанный тип цены. Поэтому входной массив price[]  в этом случая всегда имеет корректные данные с самого первого элемента массива price[0]. Но если мы указываем, что для расчетов используются данные другого индикатора, то это уже не гарантировано.

Параметр begin функции OnCalculate()

Проверим значения, которые содержит массив price[], если расчет производится на значениях другого индикатора. Для этого добавим в функцию OnCalculate() небольшой блок кода, который выведет интересующие нас значения. Теперь начало функции OnCalculate() выглядит так:

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,    // размер массива price[];
                 const int prev_calculated,// количество доступных баров ;
                                           // на предыдущем вызове;
                 const int begin,          // с какого индекса в массиве 
                                           // price[] начинаются достоверные данные;
                 const double &price[])    // массив, по которому и будет считаться индикатор;
  {
//--- флаг для однократного вывода значений price[]
   static  bool printed=false;
//--- если begin не равен нулю, значит, есть значения, которые мы не должны принимать в расчет
   if(begin>0 && !printed)
     {
      //--- выведем
      Print("Значения для расчетов начинаются с индекса begin =",begin,
            "   размер массива price[] =",rates_total);

      //--- посмотрим как выглядят значения, которые нельзя принимать для расчетов
      for(int i=0;i<=begin;i++)
        {
         Print("i =",i,"  value =",price[i]);
        }
      //--- установим признак того, что значения в лог уже выводили
      printed=true;
     }

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


Пустые значения в индикаторных буферах и DBL_MAX

Первые 14 элементов массива price[] с индексами от 0 до 13 включительно содержат одно и то же значение, равное 1.797693134862316e+308. Вы будете часто встречаться с этим числом, потому что это значение встроенной константы EMPTY_VALUE, специально предусмотренной для выделения пустых значений в индикаторном буфере.

Заполнять пустые значения нулями не является универсальным решением, так как некоторые индикаторы выдают это значение  как допустимое.  Именно по этой причине все встроенные в терминал индикаторы выдают для пустых значений именно это число. Величина 1.797693134862316e+308 для этих целей была выбрана не случайно, так как это максимально возможное значение для типа double и для  удобства в языке MQL5 оно представлено специальной константой DBL_MAX.

Если вы хотите проверить, не является ли некое число типа double пустым значение, то вы можете сравнить его либо с константой EMPTY_VALUE либо с константой DBL_MAX. Оба варианта равносильны, но использование константы EMPTY_VALUE для задания пустого значения предпочтительнее для лучшей читаемости кода.

//+------------------------------------------------------------------+
//| возвращает true для "пустых" значений                            |
//+------------------------------------------------------------------+
bool isEmptyValue(double value_to_check)
  {
//--- если число равно DBL_MAX, значит это пустое значение
   if(value_to_check==EMPTY_VALUE) return(true);
//--- не равно DBL_MAX
   return(false);
  }

DBL_MAX - очень огромное число, и индикатор RSI просто не может выдавать такие значения! И только пятнадцатый элемент массива с индексом 14 имеет понятное значение 50. Таким образом, даже не зная ничего об индикаторе, на значениях которого мы будем вычислять значения своего индикатора, с помощью параметра begin мы можем правильно организовать обработку таких случаев. А точнее, нам требуется не использовать эти пустые значения в своих расчетах.

Как связаны параметр begin и свойство PLOT_DRAW_BEGIN

Необходимо отметить тесную связь между параметром begin, передаваемым в функцию OnCalculate(), и командой на запрет отрисовки графического построения с индексом 0 на первых N барах индикатора. Если мы посмотрим на код пользовательского индикатора RSI, идущего в поставке терминала MetaTrader 5, то увидим в функции OnInit() такую запись:

//--- sets first bar from what index will be drawn
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,ExtPeriodRSI);

которая означает, что графическое построение с индексом 0 начинает отрисовываться только с бара под номером ExtPeriodRSI (это input-переменная, задающая период индикатора RSI), на всех более ранних барах отрисовки нет.

В языке MQL5 расчет индикатора A на основании данных индикатора B всегда производится на значениях нулевого индикаторного буфера индикатора B. Именно значения нулевого буфера индикатора B передаются в качестве входного параметра priсe[] функции OnCalculate() внутри индикаторе А. При этом по определению нулевой индикаторный буфер всегда привязывается к нулевому графическому построению с помощью функции SetIndexBuffer(). Поэтому выведем строгое

Правило передачи PLOT_DRAW_BEGIN в параметр begin: Значение входного параметра begin в OnCalculate(), при расчетах пользовательского индикатора A на данных другого (базового) индикатора B, всегда равно значению свойства PLOT_DRAW_BEGIN нулевого графического построения базового индикатора B.

Таким образом, если мы создали на графике индикатор RSI (индикатор B) с периодом 14 и затем на данных индикатора RSI(14) построили наш пользовательский индикатор True Strength Index (индикатор A), то:

  • индикатор RSI(14) начинает отрисовываться с 14-го бара (PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,14);) ;
  • входной массив price[] в функции OnCalculate() содержит значения нулевого буфера индикатора RSI;
  • значение параметра begin функции OnCalculate() в индикаторе TSI берется из свойства PLOT_DRAW_BEGIN нулевого графического построения индикатора RSI.

Напомним, что индикатор TSI начинает отрисовываться не с самого начала графика, т.к. для некоторых первых баров значение индикатора не определено. Индекс первого бара, с которого начинается отображение линии в индикаторе TSI, равен r+s-1, где:

  • r - период первого экспоненциального сглаживания массивов MTMBuffer[] и AbsMTMBuffer[] в соответствующие массивы EMA_MTMBuffer[] и EMA_AbsMTMBuffer[];
  • s - период последующего сглаживания массивов EMA_MTMBuffer[] и EMA_AbsMTMBuffer[].

Для баров с индексами меньше r+s-1 значения для вывода индикатора TSI на экран отсутствуют. Поэтому начало данных в конечных массивах EMA2_MTMBuffer[] и EMA2_AbsMTMBuffer[], на основании которых вычисляется индикатор TSI, получает дополнительное смещение и начинается с индекса r+s-1. Более детальные объяснения можно получить в статье "Как написать индикатор в MQL5".

Чтобы запретить отрисовку для первых r+s-1 баров, в функции OnInit() есть такая запись:

//--- с какого бара начнет отрисовываться индикатор
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,r+s-1);

Но так как начало входных данных для расчета индикатора сдвинулось на begin баров вперед, то необходимо это учесть и увеличить на begin позицию начала отрисовываемых данных в функции OnCalculate():

   if(prev_calculated==0)
     { 
      //--- увеличим позицию начала данных на begin баров, вследствие расчетов на данных другого индикатора
      if(begin>0)PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,begin+r+s-1);
     }

Теперь мы не только учитываем параметр begin для расчета значений индикатора TSI, но и в случае расчета другого индикатора на данных нашего индикатора TSI, параметр begin будет передан правильно, а именно: beginдругого_индикатора=beginнашего_индикатора+r+s-1. Поэтому, можно вывести правило наложения одного индикатора на значения другого индикатора:

Правило наложения индикаторов: Если пользовательский индикатор A начинает отрисовываться с позиции Nа (не отрисовывает первые Na значений) и строится на значениях другого индикатора B с началом отрисовки с позиции Nb, то полученный индикатор A{B} будет отрисовываться с позиции Nab=Na+Nb, где A{B} означает, что индикатор A построен на значениях нулевого индикаторного буфера индикатора B.

Таким образом, запись  TSI(25,13){RSI(14)} означает, что индикатор TSI(25,13) построен на значениях индикатора RSI(14), и в результате наложения начало данных теперь приходится на индекс (25+13-1)+14=51. То есть, отрисовка значения этого индикатора на графике начнется с 52-го бара (индексация баров начинается с 0).

Добавим значения begin при расчетах значения индикатора

Теперь мы точно знаем, что значащие значения в массиве price[] всегда начинаются с позиции begin. Начнем изменения поэтапно, сначала идет  блок вычисления значений массивов MTMBuffer[] и AbsMTMBuffer[]. Без учета параметра begin заполнение начиналось с индекса 1.

//--- рассчитать значения mtm и |mtm|
   int start;
   if(prev_calculated==0) start=1;  // начнем заполнять MTMBuffer[] и AbsMTMBuffer[]  с 1-го индекса 
   else start=prev_calculated-1;    // установим start равным последнему индексу в массивах 
   for(int i=start;i<rates_total;i++)
     {
      MTMBuffer[i]=price[i]-price[i-1];
      AbsMTMBuffer[i]=fabs(MTMBuffer[i]);
     }

Теперь мы начнем с позиции (begin+1), и блок будет выглядеть так (изменения выделены жирным шрифтом):

//--- рассчитать значения mtm и |mtm|
   int start;
   if(prev_calculated==0) start=begin+1;  // начнем заполнять MTMBuffer[] и AbsMTMBuffer[]  с индекса begin+1
   else start=prev_calculated-1;          // установим start равным последнему индексу в массивах 
   for(int i=start;i<rates_total;i++)
     {
      MTMBuffer[i]=price[i]-price[i-1];
      AbsMTMBuffer[i]=fabs(MTMBuffer[i]);
     }

Так как значения от price[0] до price[begin-1] нельзя принимать во внимание для начала расчетов, мы начинаем отталкиваться от price[begin], и первые значения, которые мы можем рассчитать для массивов MTMBuffer[] и AbsMTMBuffer[] будут соответственно:

      MTMBuffer[begin+1]=price[begin+1]-price[begin];
      AbsMTMBuffer[begin+1]=fabs(MTMBuffer[begin+1]);

Именно поэтому в цикле for переменная start теперь имеет начальное значение start=begin+1.

Учитываем begin и для зависимых массивов

Далее идет блок экспоненциального сглаживания массивов MTMBuffer[] и AbsMTMBuffer[]. Правило тут такое же простое: если начальная позиция расчетов в базовом массиве увеличилась на begin баров, то во всех зависящих от него массивах также увеличиваем начальную позицию на begin баров.

//--- рассчитаем первую скользящую среднюю на массивах
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         1,     // с какого индекса есть значения в массиве для сглаживания 
                         r,     // период экспоненциальной средней
                         MTMBuffer,       // буфер для взятия средней
                         EMA_MTMBuffer);  // в этот буфер помещаем значения средней
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         1,r,AbsMTMBuffer,EMA_AbsMTMBuffer);

Здесь базовыми массивами являются MTMBuffer[] и AbsMTMBuffer[], и рассчитанные нами значения в этих массивах теперь начинаются с индекса на begin больше. Поэтому просто добавим это смещение для функции ExponentialMAOnBuffer(). Теперь этот блок выглядит так:

//--- рассчитаем первую скользящую среднюю на массивах
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+1,// с какого индекса есть значения в массиве для сглаживания 
                         r,      // период экспоненциальной средней
                         MTMBuffer,       // буфер для взятия средней
                         EMA_MTMBuffer);  // в этот буфер помещаем значения средней
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+1,r,AbsMTMBuffer,EMA_AbsMTMBuffer);

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

Было:

//--- рассчитаем вторую скользящую среднюю на массивах
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         r,s,EMA_MTMBuffer,EMA2_MTMBuffer);
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         r,s,EMA_AbsMTMBuffer,EMA2_AbsMTMBuffer);

Стало:

//--- рассчитаем вторую скользящую среднюю на массивах
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+r,s,EMA_MTMBuffer,EMA2_MTMBuffer);
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+r,s,EMA_AbsMTMBuffer,EMA2_AbsMTMBuffer);

Последний блок расчетов изменяем аналогично. До правки кода:

//--- теперь вычислим значения индикатора
   if(prev_calculated==0) start=r+s-1; // установим начальный индекс для входных массивов
   else start=prev_calculated-1;       // установим start равным последнему индексу в массивах 
   for(int i=start;i<rates_total;i++)
     {
      TSIBuffer[i]=100*EMA2_MTMBuffer[i]/EMA2_AbsMTMBuffer[i];
     }

После правки:

//--- теперь вычислим значения индикатора
   if(prev_calculated==0) start=begin+r+s-1; // установим начальный индекс для массива значений индикатора
   else start=prev_calculated-1;             // установим start равным последнему индексу в массивах 
   for(int i=start;i<rates_total;i++)
     {
      TSIBuffer[i]=100*EMA2_MTMBuffer[i]/EMA2_AbsMTMBuffer[i];
     }

На этом доводка индикатора завершена, теперь он будет пропускать первые begin пустых значений во входном массиве price[] в обработчике OnCalculate() и учитывать вызванное этим пропуском  смещение. Но мы должны помнить о том, что на значениях индикатора TSI также могут рассчитываться другие индикаторы. Поэтому установим пустые значения нашего индикатора равными константе EMPTY_VALUE.

Нужна ли инициализация индикаторных буферов?

Массивы в MQL5 не инициализируются по умолчанию каким-либо значением, и это безусловно относится также и к массивам, которые назначаются индикаторным буферам функцией  SetIndexBuffer(). Если массив является индикаторным буфером, то его размер в процессе работы будет зависеть от параметра rates_total в обработчике OnCalculate(). 

Может возникнуть соблазн инициализировать все индикаторные буферы пустым значением EMPTY_VALUE с помощью функции ArrayInitialize(), например, однократно в начале OnCalculate()
//--- если это первый вызов OnCalculate() 
   if(prev_calculated==0)
     {
      ArrayInitialize(TSIBuffer,EMPTY_VALUE);
     }

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

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

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

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

Например, мы установили для графического построения в качестве пустого значения функцией PlotIndexSetDouble() нулевое значение:
   PlotIndexSetDouble(номер_графического_стиля,PLOT_EMPTY_VALUE,0);   

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

   if(divider==0)
      IndicatorBuffer[i]=0;
   else
      IndicatorBuffer[i]=... 

Кроме того, если для графического построения указан параметр DRAW_BEGIN, то все значения соответствующего индикаторного буфера с индексом от 0 до DRAW_BEGIN будут автоматически заполнены нулем.

Заключение

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

  1. Пустые значения во встроенных индикаторах заполнены значениями константы EMPTY_VALUE, которая точно равна максимальному значению для типа double (DBL_MAX).
  2. Для получения информации о том, с какого индекса начинаются значащие значения индикатора, необходимо анализировать входной параметр begin в функции OnCalculate() краткой формы вызова.
  3. Для того, чтобы запретить отрисовку первых N значений для указанного графического построения, задайте параметр DRAW_BEGIN с помощью функции
    PlotIndexSetInteger(номер_графического_построения,PLOT_DRAW_BEGIN,N);
  4. Если для графического построения указан параметр DRAW_BEGIN, то значения с индексом от 0 до DRAW_BEGIN автоматически будут заполнены пустыми значениями (по умолчанию это EMPTY_VALUE).
  5. В функции OnCalculate() добавьте дополнительный сдвиг на begin баров, чтобы можно было корректно рассчитать значения вашего индикатора на данных другого индикатора
    //--- если это первый вызов 
       if(prev_calculated==0)
         { 
          //--- увеличим позицию начала данных на begin баров, вследствие расчетов на данных другого индикатора
          if(begin>0)PlotIndexSetInteger(номер_графического_построения,PLOT_DRAW_BEGIN,begin+N);
         }
    
  6. Вы можете указать в обработчике OnInit() в качестве неотрисовываемых значений свое пустое значение, отличное от EMPTY_VALUE, с помощью
    PlotIndexSetDouble(номер_графического_построения,PLOT_EMPTY_VALUE,пустое_значение);
  7. Не полагайтесь на однократную инициализацию индикаторных буферов выражением
     ArrayInitialize(индикаторный_буфер,значение);
        
    Все значения индикаторного буфера в процессе работы функции OnCalculate() обязательно и последовательно задавайте явно, в том числе и пустые значения.

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

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (26)
Alexey Viktorov
Alexey Viktorov | 31 янв. 2017 в 19:31
Комбинатор:
Да, вроде того.
По сравнению с iMAOnArray() из mql4 это танцы с бубном.



А вот такая бредовая идея: Если индикатор прицепить в качестве ресурса, можно-ли получить его хендл? Не получится-ли скрестить. Чтобы не таскать два индикатора, сам индикатор и индикатор от этого индикатора. Я не практиковал ресурсы, потому мне трудно проверить, вот и спрашиваю.
Maxim Dmitrievsky
Maxim Dmitrievsky | 17 мар. 2017 в 15:27
Alexey Viktorov:
По сравнению с iMAOnArray() из mql4 это танцы с бубном.



А вот такая бредовая идея: Если индикатор прицепить в качестве ресурса, можно-ли получить его хендл? Не получится-ли скрестить. Чтобы не таскать два индикатора, сам индикатор и индикатор от этого индикатора. Я не практиковал ресурсы, потому мне трудно проверить, вот и спрашиваю.

По-моему это реально танцы с бубном, в текущей реализации :)
potom
potom | 15 окт. 2017 в 18:58
А как добавить уже готовому индикатору функцию что бы он строился на значениях другого индикатора? К примеру ADX или MACD или ZigZag... Я не могу понять как это сделать
Rashid Umarov
Rashid Umarov | 16 окт. 2017 в 11:03
potom:
А как добавить уже готовому индикатору функцию что бы он строился на значениях другого индикатора? К примеру ADX или MACD или ZigZag... Я не могу понять как это сделать

Посмотрите справку - https://www.mql5.com/ru/docs/basis/function/events#oncalculate


Vladimir Pavlov
Vladimir Pavlov | 22 нояб. 2017 в 12:23

Я повторюсь с вопросом, только немного его видоизменю, потому-что в документации ответ основан на вызове стандартных индикаторов через меню.

А меня интересует доступ у данным другого индикатора открытого на графике или в подокне из программного кода.

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

Магия фильтрации Магия фильтрации
Большинство разработчиков механических торговых систем (МТС), так или иначе, использует фильтрацию торговых сигналов. В статье рассмотрены создание и применение полосовых и дискретных фильтров в советниках для улучшения характеристик МТС.
Как за 10 минут написать DLL библиотеку для MQL5 и обмениваться данными? Как за 10 минут написать DLL библиотеку для MQL5 и обмениваться данными?
Так уж сложилось, что сейчас мало кто из разработчиков помнит, как написать простую DLL библиотеку и в чем особенности связывания разнородных систем. Я постараюсь за 10 минут на примерах продемонстрировать весь процесс создания простых DLL библиотек и раскрою некоторые технические детали нашей реализации связывания. Покажу пошаговый процесс создания DLL библиотеки в Visual Studio с примерами передачи разных типов переменных (числа, массивы, строки и т.д.) и защиту клиентского терминала от падений в пользовательских DLL.
МetaTrader 5. Экспорт котировок в .NET приложение, используя WCF сервисы МetaTrader 5. Экспорт котировок в .NET приложение, используя WCF сервисы
Вам необходимо организовать трансляцию котировок из MetaTrader 5 в собственное приложение? Связка MQL5-DLL позволяет создавать подобные решения. В статье продемонстрирован один из способов трансляции котировок из MetaTrader 5 в приложения, написанные на .NET. Мне было рациональнее, интереснее и проще реализовать экспорт котировок именно с использованием этой платформы. К сожалению, с выходом "пятерки" поддержки .Net также не появилось, поэтому по старинке будем использовать как прослойку win32 dll с поддержкой .NET.
Инструмент "Ценовая гистограмма" (Рыночный профиль) и его реализация на MQL5 Инструмент "Ценовая гистограмма" (Рыночный профиль) и его реализация на MQL5
Рыночный профиль был разработан Питером Стидлмайером (Peter Steidlmayer), который предложил использовать альтернативное представление информации как о горизонтальном, так и о вертикальном движении рынка, что дает полностью отличный набор моделей. Он предположил, что у рынка существует основной рыночный пульс, или фундаментальная модель, которая называется цикл равновесия и неравновесия (cycle of equilibrium and disequilibrium). В данной статье я сделаю попытку дать общие понятия об упрощенной модели Рыночного профиля (Market Profile) – Ценовой Гистограмме (Price Histogram) и расскажу, как реализовал данный инструмент на MQL5.