Скачать MetaTrader 5

Строим анализатор спектра

21 октября 2010, 10:29
Victor
6
3 919

Введение

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

К статье прилагаются три файла

  1. SpecAnalyzer.mq5 – индикатор, рассматриваемый в данной статье.
  2. SpecAnalyzer.mqh – включаемый файл для SpecAnalyzer.mq5.
  3. SAInpData.mq5 – индикатор, используемый для организации доступа к внешним данным.

Для нормальной загрузки индикатора SpecAnalyzer все три файла должны быть скопированы в каталог \MQL5\Indicators терминала. После чего, файлы SpecAnalyzer.mq5 и SAInpData.mq5 должны быть откомпилированы. Индикатор предполагается загружать в основное окно графика. После загрузки индикатор изменяет установки связанные с режимом отображения в этом окне, а при выгрузке индикатора в окне будут удалены все графические объекты. Поэтому, для того чтобы случайно не изменить режим отображения в существующих окнах терминала, индикатор желательно загружать в отдельное, специально для него созданное окно.

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

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

Координаты

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

Например, для размещения на графике таких объектов, как “Label” или “Button”, необходимо задавать их координаты в количестве пикселей относительно одного из углов окна графика. При таком способе адресации объекты сохраняют свое положение независимо от текущих свойств окна и установленного в этом окне масштаба отображения графика. Даже при изменении размеров самого окна, такие объекты не меняют своего положения и сохраняют привязку друг к другу. При сдвиге левой кнопкой мышки отображаемого в окне графика, эти объекты так же сохраняют свое положение относительно выбранной в окне точки привязки.

Другая группа графических объектов предполагает привязку не к координатам окна, а графику, выводимому в этом окне. Это такие объекты, как “Trend Line”, “Rectangle” и так далее. В качестве координат, при создании и размещении таких объектов, используется значение времени и значение величины отображаемого в окне ценового графика. При таком способе задания координат, эти объекты вместе с изменением масштаба отображения графика или в случае его сдвига, будут изменять свое положение относительно окна и относительно друг друга. С появлением на графике нового бара объекты так же изменят свое положение, потому что шкала времени графика при этом сдвигается на величину таймфрейма влево.

В индикаторе SpecAnalyzer, для создания панели управления анализатором спектра, одновременно используются оба типа рассмотренных графических объектов. Для того чтобы объекты имеющие привязку к графику не смещались относительно окна, устанавливается фиксированный режим отображения графика по вертикальной шкале, и режим соответствующий минимально возможному масштабу отображения по горизонтальной шкале графика. Кроме того, фиксированный минимум вертикальной шкалы устанавливается в 0.0, а для горизонтальной шкалы графика выбирается режим, при котором отсутствует отступ нулевого бара от правого края и режим автоматического перехода к правому краю графика. Таким образом, точка координат соответствующая нулевому бару и значению 0.0 графика оказывается в правом нижнем углу и при фиксированном масштабе отображения ее можно принять за точку привязки при задании координат объектам типа “Trend Line” и “Rectangle”. Если при этом и для графических объектов типа “Label” или “Button” в качестве точки привязки тоже назначить нижний правый угол графика, то оба типа размещаемых объектов будут однозначно привязаны друг к другу.

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

void GRaphChart::SetOwnProperty(void)
  {
  ChartSetInteger(m_chart_id,CHART_FOREGROUND,1);
  ChartSetInteger(m_chart_id,CHART_SHIFT,0);
  ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,0);
  ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,1);
  ChartSetInteger(m_chart_id,CHART_SCALE,0);
  ChartSetInteger(m_chart_id,CHART_SCALEFIX_11,1);
  ChartSetInteger(m_chart_id,CHART_SHOW_OHLC,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_BID_LINE,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_ASK_LINE,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_LAST_LINE,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_PERIOD_SEP,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_GRID,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_VOLUMES,CHART_VOLUME_HIDE);
  ChartSetInteger(m_chart_id,CHART_SHOW_OBJECT_DESCR,0);
  ChartSetInteger(m_chart_id,CHART_SHOW_TRADE_LEVELS,0);
  ChartSetInteger(m_chart_id,CHART_COLOR_BACKGROUND,Black);
  ChartSetInteger(m_chart_id,CHART_COLOR_FOREGROUND,Black);
  ChartSetDouble(m_chart_id,CHART_FIXED_MIN,0.0);
  }

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

Так как в процессе работы индикатор SpecAnalyzer изменяет свойства графика, необходимо обеспечить сохранения предыдущих свойств при его загрузке и их восстановление при его выгрузке. В стандартной библиотеке MQL5 для этой цели предусмотрены виртуальные функции Save() и Load() являющиеся членами класса CChart. Функции служат для сохранения в файл свойств объекта класса CChart и восстановления этих свойств из созданного файла. Чтобы изменить состав сохраняемых свойств и избежать использования файловых операций при сохранении свойств графика, виртуальные функции Save() и Load() класса CChart были переопределены при создании нового класса GRaphChart (смотри файл SpecAnalyzer.mqh). 

class GRaphChart : public CChart
  {
  protected:
    struct ChartPropertyes
      {
      double shift_size;
      double fixed_max;
      double fixed_min;
      double points_per_bar;
      long   mode;
      bool   foreground;
      bool   shift;
      bool   autoscroll;
      long   scale;
      bool   scalefix;
      bool   scalefix_11;
      bool   scale_pt_per_bar;
      bool   show_ohls;
      bool   show_bid_line;
      bool   show_ask_line;
      bool   show_last_line;
      bool   show_period_sep;
      bool   show_grid;
      long   show_volumes;
      bool   show_object_descr;
      bool   show_trade_levels;
      long   color_background;
      long   color_foreground;
      long   color_grid;
      long   color_volume;
      long   color_chart_up;
      long   color_chart_down;
      long   color_chart_line;
      long   color_candle_bull;
      long   color_candle_bear;
      long   color_bid;
      long   color_ask;
      long   color_last;
      long   color_stop_level;
      string ch_comment;
      };
      ChartPropertyes ChProp;
  
  public:
                   GRaphChart();
                  ~GRaphChart();
                   
         void      SetOwnProperty();
  virtual void      Save();
  virtual void      Load();
  };

Базовым для GRaphChart является класс CChart стандартной библиотеки MQL5. В классе GRaphChart определена структура ChartPropertyes и создан объект ChProp, предназначенный для хранения свойств графика в памяти, а не в файле как это реализовано в базовом классе. Функция Save() заполняет структуру ChProp данными в соответствии со значением текущих свойств графика, а функция Load() восстанавливает из нее ранее сохраненные свойства графика.

Функция Save() вызывается в конструкторе класса GRaphChart, а функция Load() в его деструкторе. Поэтому сохранение и восстановление прежнего состояния графика происходит автоматически при создании и уничтожении объекта класса GRaphChart. В конструкторе класса также вызывается функция SetOwnProperty(), которая упоминалась ранее.

//---------------------------------------------------- Constructor GRaphChart --
GRaphChart::GRaphChart()
  {
  m_chart_id=ChartID();
  Save();                                // Keep a original chart settings
  SetOwnProperty();                             // Set own chart settings
  }
//----------------------------------------------------- Destructor GRaphChart --
GRaphChart::~GRaphChart()
  {
  Load();                             // Restore a previous chart settings
  m_chart_id=-1;
  }

Продемонстрируем использование класса GRaphChart на простом примере. Для этого создадим в MetaEditor новый пользовательский индикатора с именем Test. Включим в текст этого индикатора заголовочный файл SpecAnalyzer.mqh и создадим объект класса GRaphChart, добавив в него две строчки.

//+------------------------------------------------------------------+
//|                                                         Test.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2010, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

#include "SpecAnalyzer.mqh"    // <----- Включение заголовочного файла 

GRaphChart  MainChart; // <----- Создание объекта класса GRaphChart

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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 value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Для успешной компиляции приведенного кода, файл SpecAnalyzer.mqh должен быть размещен в каталоге \MQL5\Indicators терминала.

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

Вернемся к индикатору SpecAnalyzer. В начале программы индикатора (смотри файл SpecAnalyzer.mq5) производится создание объекта MainChart класса GRaphChart, что приводит при загрузке индикатора к сохранению свойств графика.

                 . . .
GRaphChart MainChart; // Create MainChart object
                 . . .

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

Панель управления

Внешний вид панели управления в индикаторе SpecAnalyzer полностью определяется размещенными в окне графическими объектами. Класс AllGrObject объединяет все функции, необходимые для создания и взаимодействия с ними, смотри файл SpecAnalyzer.mqh.

class AllGrObject : public CChart
  {
  protected:
    long      m_chart_id;                                    // chart idintifier
    
  public:
              AllGrObject();
             ~AllGrObject();
                   
    void      AddLabel(string name,int fsize,string font,
                             color col,int x,int y,string text="");
    void      AddButton(string name,int xsize,int ysize,color bgcol,
                        int fsize,string font,color col,int x,int y,
                        string text="",int state=0);
    void      AddEdit(string name,int xsize,int ysize,color bgcol,int fsize,
                      string font,color col,int x,int y,string text="");
    void      AddTrendLine(string name,color col,int style=0,int width=1);
    void      AddArrowline(string name,color col,int style=0,int width=1);
    void      AddRectangle(string name,color col,int style=0,int width=1);
    void      MoveGrObject(string name,int x1,int y1,int x2,int y2);
    void      SetButtonProp(string name,bool state,color col);
    long      GetRowNumber(string name);
    void      LabelTxt(string name,string str);
    
  };

Функции класса, имя которых начинается с Add, служат для создания графических объектов. Например, AddButton() создает объект “Button”.

В программе всем графическим объектам координаты задаются в форме смещения на количество пикселей относительно нижнего правого угла графика. Для объектов “Trend Line”, “Arrowed Line” и “Rectangle” необходимо преобразовывать эти координаты в значение времени и цены. Такое преобразование, перед присвоением координат объекту, производится в функции MoveGrObject(). Одному пикселю по горизонтали в соответствие ставится один бар, а одному пикселю по вертикали – один пункт.

void AllGrObject::MoveGrObject(string name,int x1,int y1,int x2,int y2)
  {
  datetime t1[1],t2[1];
  long typ;
  
  typ=ObjectGetInteger(m_chart_id,name,OBJPROP_TYPE);
  if(typ==OBJ_TREND||typ==OBJ_ARROWED_LINE||typ==OBJ_RECTANGLE)
    {
    CopyTime(_Symbol,_Period,x1,1,t1);
    CopyTime(_Symbol,_Period,x2,1,t2);
    ObjectSetInteger(m_chart_id,name,OBJPROP_TIME,0,t1[0]);
    ObjectSetDouble(m_chart_id,name,OBJPROP_PRICE,0,_Point*y1);
    ObjectSetInteger(m_chart_id,name,OBJPROP_TIME,1,t2[0]);
    ObjectSetDouble(m_chart_id,name,OBJPROP_PRICE,1,_Point*y2);
    }
  }

Все графические объекты создаются в индикаторе только один раз, это происходит при вызове gr_object_create() из функции индикатора OnInit(), смотри файл SpecAnalyzer.mq5. Для всех объектов, кроме “Trend Line”, “Arrowed Line” и “Rectangle”, сразу задаются их координаты. Для объектов типа “Trend Line”, “Arrowed Line” и “Rectangle” координаты задаются вызовом функции gr_object_coordinate(), которая для задания координат использует упомянутую ранее функцию MoveGrObject(), преобразующую способ адресации. 

void gr_object_coordinate()
  {
  GObj.MoveGrObject("Trdline1",48,150,48,360);
  GObj.MoveGrObject("Trdline2",176,150,176,360);
  GObj.MoveGrObject("Trdline3",304,150,304,360);
  GObj.MoveGrObject("Trdline4",432,150,432,360);
  GObj.MoveGrObject("Trdline5",42,350,560,350);
  GObj.MoveGrObject("Trdline6",42,300,560,300);
  GObj.MoveGrObject("Trdline7",42,250,560,250);
  GObj.MoveGrObject("Trdline8",42,200,560,200);
  GObj.MoveGrObject("Arrline1",560,150,28,150);
  GObj.MoveGrObject("Arrline2",560,150,560,370);
  GObj.MoveGrObject("Rect1",0,1,208,110);
  GObj.MoveGrObject("Rect2",208,1,416,110);
  GObj.MoveGrObject("Rect3",416,1,624,110);
  GObj.MoveGrObject("Rect4",0,1,624,400);
  GObj.MoveGrObject("Rect5",20,10,195,80);
  }

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

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

  1. Amplitude/Line - вывод модуля преобразования Фурье при линейной шкале по оси Y.
  2. Amplitude/dB - вывод модуля преобразования Фурье при логарифмическом масштабе шкалы по оси Y.
  3. Power/Line - вывод квадрата модуля преобразования Фурье при линейной шкале по оси Y.
  4. Power/dB - вывод квадрата модуля преобразования Фурье при логарифмическом масштабе шкалы по оси Y.

Для обработки нажатий на кнопки этой группы в функцию индикатора OnChartEvent() включен следующий фрагмент кода: 

  if(id==CHARTEVENT_OBJECT_CLICK)                       // Click on the gr. object
    {
    if(sparam=="Butt1")                                   // Click on the button
      {
      GObj.SetButtonProp("Butt1",1,Chocolate);
      GObj.SetButtonProp("Butt2",0,SlateGray);
      GObj.SetButtonProp("Butt3",0,SlateGray);
      GObj.SetButtonProp("Butt4",0,SlateGray);
      YPowerFlag=0;YdBFlag=0;
      }
    if(sparam=="Butt2")                                   // Click on the button
      {
      GObj.SetButtonProp("Butt1",0,SlateGray);
      GObj.SetButtonProp("Butt2",1,Chocolate);
      GObj.SetButtonProp("Butt3",0,SlateGray);
      GObj.SetButtonProp("Butt4",0,SlateGray);
      YPowerFlag=0;YdBFlag=1;
      }
    if(sparam=="Butt3")                                   // Click on the button
      {
      GObj.SetButtonProp("Butt1",0,SlateGray);
      GObj.SetButtonProp("Butt2",0,SlateGray);
      GObj.SetButtonProp("Butt3",1,Chocolate);
      GObj.SetButtonProp("Butt4",0,SlateGray);
      YPowerFlag=1;YdBFlag=0;
      }
    if(sparam=="Butt4")                                   // Click on the button
      {
      GObj.SetButtonProp("Butt1",0,SlateGray);
      GObj.SetButtonProp("Butt2",0,SlateGray);
      GObj.SetButtonProp("Butt3",0,SlateGray);
      GObj.SetButtonProp("Butt4",1,Chocolate);
      YPowerFlag=1;YdBFlag=1;
      }

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

Вторая группа из четырех кнопок обеспечивает выбор источника входных данных. Это могут быть внешние данные, полученные через вызов индикатора SAInpData.mq5 или три тестовые последовательности, генерируемые самой программой. Последняя группа кнопок включает в себя две кнопки, используемые для прокрутки списка в поле вывода текстовой информации. Обработка нажатия на все эти кнопки так же осуществляется в функции индикатора OnChartEvent() аналогично кнопкам первой группы, смотри файл SpecAnalyzer.mq5.

Приведем пример использования функций класса AllGrObject, воспользовавшись для этого ранее созданным тестовым индикатором Test.mq5. Для этого дополним его исходный текст еще несколькими строками и включим в него из файла SpecAnalyzer.mq5 функцию gr_object_create() и функцию gr_object_coordinate() о которых упоминалось ранее.

//+------------------------------------------------------------------+
//|                                                         Test.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2010, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

#include "SpecAnalyzer.mqh" 

GRaphChart  MainChart;

AllGrObject GObj;        // <----- Создание объекта класса AllGrObject

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
//---

  gr_object_create();          // <----- создание графических объектов

   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//---

  MainChart.SetOwnProperty();    // <----- восстановление текущих свойст графика
  gr_object_coordinate();     // <----- установка координат графическим объектам


//--- return value of prev_calculated for next call
   return(rates_total);
  }

//----------------------------------------------- Create all graphical object --
void gr_object_create()
  {
  GObj.AddLabel("Title",10,"Arial",DarkGray,256,367,"Spectrum Analyzer");
  GObj.AddLabel("Label1",9,"Arial",LightSteelBlue,557,128,"0");
  GObj.AddLabel("Label2",9,"Arial",LightSteelBlue,422,128,"128");
  GObj.AddLabel("Label3",9,"Arial",LightSteelBlue,294,128,"256");
  GObj.AddLabel("Label4",9,"Arial",LightSteelBlue,166,128,"384");
  GObj.AddLabel("Label5",9,"Arial",LightSteelBlue,40,128,"512");
  GObj.AddLabel("Label6",9,"Arial",LightSteelBlue,28,156,"N");
  GObj.AddLabel("Label7",9,"Arial",LightSteelBlue,569,141,"0.00");
  GObj.AddLabel("Label8",9,"Arial",LightSteelBlue,569,191,"0.25");
  GObj.AddLabel("Label9",9,"Arial",LightSteelBlue,569,241,"0.50");
  GObj.AddLabel("Label10",9,"Arial",LightSteelBlue,569,291,"0.75");
  GObj.AddLabel("Label11",9,"Arial",LightSteelBlue,569,341,"1.00");
  GObj.AddLabel("Label12",9,"Arial",LightSteelBlue,569,358,"U");
  GObj.AddLabel("Label13",9,"Arial",DarkGray,490,86,"Y-axis Mode");
  GObj.AddLabel("Label14",9,"Arial",DarkGray,285,86,"Input Data");
  GObj.AddLabel("Label15",9,"Arial",DarkGray,75,86,"Level");
  GObj.AddLabel("Label16",9,"Arial",DarkGray,185,86,"N");
  GObj.AddLabel("Label17",8,"Courier",DarkOliveGreen,64,64);
  GObj.AddLabel("Label18",8,"Courier",DarkOliveGreen,64,51);
  GObj.AddLabel("Label19",8,"Courier",DarkOliveGreen,64,38);
  GObj.AddLabel("Label20",8,"Courier",DarkOliveGreen,64,25);
  GObj.AddLabel("Label21",8,"Courier",DarkOliveGreen,64,12);
  GObj.AddButton("Butt1",185,18,C'32,32,32',8,"Arial",SlateGray,612,79,"Amplitude (line)",0);
  GObj.AddButton("Butt2",185,18,C'32,32,32',8,"Arial",Chocolate,612,61,"Amplitude (log)",1);
  GObj.AddButton("Butt3",185,18,C'32,32,32',8,"Arial",SlateGray,612,43,"Power (line)",0);
  GObj.AddButton("Butt4",185,18,C'32,32,32',8,"Arial",SlateGray,612,25,"Power (log)",0);
  GObj.AddButton("Butt5",185,18,C'32,32,32',8,"Arial",SlateGray,403,79,"External Data",0);
  GObj.AddButton("Butt6",185,18,C'32,32,32',8,"Arial",SlateGray,403,61,"Test 1. SMA(3)",0);
  GObj.AddButton("Butt7",185,18,C'32,32,32',8,"Arial",Chocolate,403,43,"Test 2. SMA(32)",1);
  GObj.AddButton("Butt8",185,18,C'32,32,32',8,"Arial",SlateGray,403,25,"Test 3. LWMA(12)",0);
  GObj.AddButton("Butt9",14,34,C'32,32,32',8,"Wingdings",SlateGray,36,78,"\x0431",0);
  GObj.AddButton("Butt10",14,34,C'32,32,32',8,"Wingdings",SlateGray,36,44,"\x0432",0);
  GObj.AddEdit("Edit1",35,18,Black,9,"Arial",SlateGray,180,102);
  GObj.AddTrendLine("Trdline1",C'32,32,32');
  GObj.AddTrendLine("Trdline2",C'32,32,32');
  GObj.AddTrendLine("Trdline3",C'32,32,32');
  GObj.AddTrendLine("Trdline4",C'32,32,32');
  GObj.AddTrendLine("Trdline5",C'32,32,32');
  GObj.AddTrendLine("Trdline6",C'32,32,32');
  GObj.AddTrendLine("Trdline7",C'32,32,32');
  GObj.AddTrendLine("Trdline8",C'32,32,32');
  GObj.AddArrowline("Arrline1",LightSteelBlue);
  GObj.AddArrowline("Arrline2",LightSteelBlue);
  GObj.AddRectangle("Rect1",C'72,72,72');
  GObj.AddRectangle("Rect2",C'72,72,72');
  GObj.AddRectangle("Rect3",C'72,72,72');
  GObj.AddRectangle("Rect4",DarkGray);
  GObj.AddRectangle("Rect5",C'72,72,72');
  }
//---------- Set object coordinate for Trend Line, Arroved Line and Rectangle --
void gr_object_coordinate()
  {
  GObj.MoveGrObject("Trdline1",48,150,48,360);
  GObj.MoveGrObject("Trdline2",176,150,176,360);
  GObj.MoveGrObject("Trdline3",304,150,304,360);
  GObj.MoveGrObject("Trdline4",432,150,432,360);
  GObj.MoveGrObject("Trdline5",42,350,560,350);
  GObj.MoveGrObject("Trdline6",42,300,560,300);
  GObj.MoveGrObject("Trdline7",42,250,560,250);
  GObj.MoveGrObject("Trdline8",42,200,560,200);
  GObj.MoveGrObject("Arrline1",560,150,28,150);
  GObj.MoveGrObject("Arrline2",560,150,560,370);
  GObj.MoveGrObject("Rect1",0,1,208,110);
  GObj.MoveGrObject("Rect2",208,1,416,110);
  GObj.MoveGrObject("Rect3",416,1,624,110);
  GObj.MoveGrObject("Rect4",0,1,624,400);
  GObj.MoveGrObject("Rect5",20,10,195,80);
  }
//+------------------------------------------------------------------+

Для обеспечения доступа к функциям класса AllGrObject создадим объект GObj этого класса. В функцию OnInit() индикатора включим вызов функции gr_object_create() в которой создаются все необходимые графические объекты, определяющие внешний вид и функциональность создаваемой панели управления индикатором.

В функцию OnCalculate добавим вызовы функций MainChart.SetOwnProperty() и gr_object_coordinate(), таким образом, свойства графика и координаты, размещенных на нем объектов будут восстанавливаться с приходом каждого нового тика. Такое восстановление необходимо при формировании на исходном графике нового бара, сдвиге поля графика при помощи левой кнопки мышки или при изменении пользователем свойств графика. После компиляции и загрузки этого тестового примера мы увидим внешний вид нашей панели управления, смотри Рис. 1.

Рис. 1. Внешний вид панели управления. 

Рис. 1. Внешний вид панели управления.

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

 Рис. 2. Шкала графика.

Рис. 2. Шкала графика. 

Анализатор спектра

В индикаторе производится оценка спектра по 1024 отсчетам входной последовательности. Оценка спектра осуществляется при помощи алгоритма быстрого преобразования Фурье. Функция, реализующая алгоритм БПФ, взята из публикаций на сайте www.mql4.com. Для расчетов используется функция БПФ входной вещественной временной последовательности, ее код размещен в файле SpecAnalyzer.mqh. Все необходимые действия, связанные с оценкой спектра, реализованы в функции fft_calc(). 

void fft_calc()
  {
  int i,k;

  realfastfouriertransform(InpData,ArraySize(InpData),false);          // FFT
  for(i=1;i<ArraySize(Spectrum);i++)
    {
    k=i*2;
    Spectrum[i]=InpData[k]*InpData[k]+InpData[k+1]*InpData[k+1];    
    }
  Spectrum[0]=0.0;                             // Clear constant component level
  }

При вызове функции fft_calc() в массиве InpData[] должна располагаться входная анализируемая последовательность. После выполнения функции realfastfouriertransform() в этом массиве будет размещен результат БПФ. Далее, из действительной и мнимой части оценок спектра, для каждой гармоники вычисляется квадрат модуля, результат вычисления размещается в массиве Spectrum[]. Индекс элемента в массиве Spectrum[] соответствует номеру гармоники. Так как вычисленное значение величины постоянной составляющей в индикаторе не используется, элементу массива Spectrum[0] всегда присваивается нулевое значение.

В зависимости от значения переменной InputSource массив InpData[] может быть заполнен тестовыми последовательностями или данными, полученными от внешнего индикатора. Формирование входных данных происходит в функции get_input_data().

void get_input_data()
  {
  int i;
  
  ArrayInitialize(InpData,0.0);
  switch(InputSource)
    {
    case 0:                                                    // External Data
      if(ExtHandle!=INVALID_HANDLE)
        CopyBuffer(ExtHandle,0,0,ArraySize(InpData),InpData);
      break;
    case 1:                                                    // Test 1. SMA(3)
      for(i=0;i<3;i++)InpData[i]=1;
      break;
    case 2:                                                   // Test 2. SMA(32)
      for(i=0;i<32;i++)InpData[i]=1;
      break;
    case 3:                                                  // Test 3. LWMA(12)
      for(i=0;i<12;i++)InpData[i]=12-i;
      break;
    }
  }

При значении InputSource равном нулю, во входной массив InpData[] будут скопированы 1024 значения из нулевого буфера индикатора SAInpData.mq5. Данные предназначенные для анализа могут быть сформированы как в самом этом индикаторе, так и за счет вызова из него других индикаторов. Для обеспечения доступа к индикатору SAInpData.mq5, в функцию OnInit() включена следующая строчка, в которой определяется значение переменной ExtHandle.

int OnInit() 
 {
 . . .

 ExtHandle=iCustom(NULL,0,"SAInpData");  // External indicator Handle

 return(0);
 }

Индикатор SAInpData.mq5 должен располагаться в каталоге \MQL5\Indicators терминала. Прилагаемый к статье в качестве примера индикатор SAInpData.mq5 передает анализатору спектра произвольную последовательность. Для того чтобы SAInpData.mq5 стал передавать анализатору другую последовательность, необходимо изменить его исходный текст.

В качестве тестовых последовательностей в функции get_input_data() генерируются импульсные характеристики скользящих средних SMA(3), SMA(32) и LWMA(12). Учитывая, что преобразование Фурье импульсной характеристики фильтра соответствует АЧХ этого фильтра, при выборе тестовых последовательностей мы сможем наблюдать АЧХ этих скользящих средних.

Для нормирования результатов спектральной оценки хранящихся в массиве Spectrum[] и их подготовки к выводу в заданном режиме отображения используется функция norm_and_draw(), смотри файл SpecAnalyzer.mq5. В зависимости от выбранного режима отображения эта функция так же заменяет текстовую разметку шкалы Y.

Результаты оценки спектра входной последовательности выводятся не только в графической, но и в текстовой форме. Для представления результатов в текстовом виде созданы пять графических объектов типа “Label”, которые соответствуют пяти текстовым строкам вывода. Функция list_levels() осуществляет заполнение этих строк данными. 

void list_levels()
  {
  int m;
  string str;
  
  if(YdBFlag==0) str="%3u    %.5f";                     // If Y-axis mode = Line
  else str="%3u  %6.1f dB";                               // If Y-axis mode = dB
  m=ArraySize(ListArray)-5;
  if(ListPointer<1)ListPointer=1;
  if(ListPointer>m)ListPointer=m;
  GObj.LabelTxt("Label17",StringFormat(str,ListPointer,ListArray[ListPointer]));
  GObj.LabelTxt("Label18",StringFormat(str,ListPointer+1,ListArray[ListPointer+1]));
  GObj.LabelTxt("Label19",StringFormat(str,ListPointer+2,ListArray[ListPointer+2]));
  GObj.LabelTxt("Label20",StringFormat(str,ListPointer+3,ListArray[ListPointer+3]));
  GObj.LabelTxt("Label21",StringFormat(str,ListPointer+4,ListArray[ListPointer+4]));
  }

В строки выводятся отформатированные при помощи функции StringFormat() значения уровней из массива ListArray[]. Этот массив, в соответствии с текущим режимом отображения, заполняется данными в теле функции norm_and_draw(), смотри файл SpecAnalyzer.mq5. Данные из массива ListArray[] выводятся, начиная с индекса массива, равного значению, хранящемуся в переменной ListPointer. Изменить значение переменной ListPointer, а, следовательно, и начальный индекс выводимых строк, можно нажатием на расположенные справа от поля вывода кнопки или введя номер необходимого индекса в поле ввода. События, связанные с нажатием на эти кнопки и завершением редактирования в поле ввода обрабатываются в функции OnChartEvent() индикатора, смотри файл SpecAnalyzer.mq5.

Внешний вид индикатора SpecAnalyzer показан на приведенном ниже рисунке.

 Рис. 3. Внешний вид индикатора SpecAnalyzer.

Рис. 3. Внешний вид индикатора SpecAnalyzer.  

Заключение

Как уже упоминалось, индикатор SpecAnalyzer.mq5 является всего лишь прототипом полноценного анализатора спектра, и рассматривается в статье только в качестве примера использования графических объектов. Для создания более совершенного индикатора наверняка потребуется модернизация его внешнего вида, разработка более функционального интерфейса и конечно доработка алгоритма оценки спектра.

Можно значительно улучшить внешний вид индикатора, используя для его оформления графический объект “Bitmap”, создав в графическом редакторе изображение передней панели индикатора и поместив это изображение в качестве подложки, поверх которой будут отображены элементы управления. Такой подход можно использовать и для создания индикаторов со сменными скинами.

Литература

  1. Юкио Сато. Обработка сигналов.Первое знакомство.
  2. Л.Рабинер, Б.Гоулд. Теория и применение цифровой обработки сигналов.
  3. С.Л.Марпл-мл. Цифровой спектральный анализ и его приложения.

Файлы

  1. SpecAnalyzer.mq5 – индикатор, рассматриваемый в данной статье.
  2. SpecAnalyzer.mqh – включаемый файл для SpecAnalyzer.mq5.
  3. SAInpData.mq5 – индикатор, используемый для организации доступа к внешним данным.

 

Прикрепленные файлы |
SAInpData.mq5 (3.29 KB)
SpecAnalyzer.mq5 (15.39 KB)
SpecAnalyzer.mqh (20.79 KB)
Andrey Vasiliev
Andrey Vasiliev | 24 окт 2010 в 23:58

В этом месте все электронщики обычно начинают плакать от радости. Сразу вспоминается КТ315, П416, кварцевые фильтры, амплитудная и частотная модуляции, двойное преобразование частоты и т.д.


Nikolay Demko
Nikolay Demko | 26 окт 2010 в 14:19

Из статьи так и не понял для какой цели можно эту разработку применить.

Может кто то объяснит?

А то пока из пользы вижу только портирование библиотеки фурье-преобразования на МТ5.


Andrey Dik
Andrey Dik | 26 окт 2010 в 14:37
Urain:

Из статьи так и не понял для какой цели можно эту разработку применить.

Может кто то объяснит?

А то пока из пользы вижу только портирование библиотеки фурье-преобразования на МТ5.

А мне понравилось. Это же готовая аналитическая платформа с графическим интерфейсом. Можно использовать как шаблон для встраивания своих методов стат. анализа.
Nikolay Demko
Nikolay Demko | 26 окт 2010 в 16:31
joo:
А мне понравилось. Это же готовая аналитическая платформа с графическим интерфейсом. Можно использовать как шаблон для встраивания своих методов стат. анализа.

Ну шаблон я тоже почерпнул (библиотечку фурье), спасибо конечно автору, не нужно самому клаву мучать.

Я о практическом применении именно спектрографа? Если кто разобрался куда применить, поделитесь мыслью.

Andrey Dik
Andrey Dik | 26 окт 2010 в 17:14
Urain:

Ну шаблон я тоже почерпнул (библиотечку фурье), спасибо конечно автору, не нужно самому клаву мучать.

Я о практическом применении именно спектрографа? Если кто разобрался куда применить, поделитесь мыслью.

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

Может быть автор поделится соображениями о возможном прямом практическом применении библы применительно к рынку? 

Интервью с Борисом Одинцовым (ATC 2010) Интервью с Борисом Одинцовым (ATC 2010)

Борис Одинцов - один из самых ярких участников Чемпионата, преодолевший на третьей торговой неделе планку в $100 000. Стремительный взлет своего эксперта Борис объясняет благоприятным стечением обстоятельств. В этом интервью он рассказывает, каким вещам стоит уделять внимание в трейдинге и о том, какой рынок неблагоприятен для его эксперта.

Интервью с Валерием Мазуренко (ATC 2010) Интервью с Валерием Мазуренко (ATC 2010)

К концу первой торговой недели на первом месте оказался Валерий Мазуренко (notused) с мультивалютным экспертом ch2010. Ранее воспринимавший трейдинг как хобби, Валерий уже полгода пытается монетизировать свое «увлечение» и написать устойчивый советник для реальной торговли. В этом интервью экспертописатель делится своими взглядами на роль математики в трейдинге и рассказывает, почему объектно-ориентированный подход отлично подходит для написания мультивалютников.

Усреднение ценовых рядов без дополнительных буферов для промежуточных расчетов Усреднение ценовых рядов без дополнительных буферов для промежуточных расчетов

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

Взгляни на рынок через готовые классы Взгляни на рынок через готовые классы

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