English Русский 中文 Español Deutsch Português
DoEasyライブラリの時系列(第48部): 複数銘柄・複数期間指標バッファ

DoEasyライブラリの時系列(第48部): 複数銘柄・複数期間指標バッファ

MetaTrader 5 | 28 10月 2020, 09:45
464 0
Artyom Trishkin
Artyom Trishkin

内容


概念

現段階では、ライブラリには、複数期間指標バッファを作成および追跡するための機能がすでに含まれています。次に、カスタムプログラムで使用するライブラリツールの開発を目的とした追加のタスクを処理できるように、複数銘柄モードで作業するための機能を追加します。

以前の作業により、バッファオブジェクトクラスはすでにそのような機能を備えていますが、改良が必要です。したがって、本日は最後の準備をおこない、複数銘柄・複数期間標準指標の簡略化された開発に進みます。
これを実現するには、計算バッファオブジェクトクラスを改善して、標準指標ハンドルによって配列データをその配列に受け入れることができるようにする必要があります。ライブラリではこれをすでにおこなうことができますが、これから実装しようとしている小さな追加により、タスクが大幅に容易になり、任意の銘柄/期間の標準指標から現在のチャートにデータを表示できるようになります。

次の記事では、現在の概念をクラスに適用して、任意の銘柄/期間の標準指標データを操作し、複数銘柄・複数期間標準指標を作成するタスクを簡略化します。

任意の銘柄を操作するためのバッファオブジェクトクラスの改善

基本抽象バッファオブジェクトのクラスには、現在の指標バッファの後に作成できる後続指標バッファの配列インデックス値が含まれています。この現在のオブジェクトでは、現在のバッファオブジェクトのタイプとその描画スタイルに応じて、後続指標バッファのインデックスの単純な計算に頼る必要がありますが、計算では、「fill with color between two levels」(2つのレベルの間の色で塗りつぶす)描画スタイルの指標バッファにカラーバッファがないことも考慮されます。

後続バッファのインデックスを計算するタスクを簡素化し、さまざまな要因を考慮したときに計算の明確さを妨げないようにするために、バッファオブジェクトの構築に使用されるすべての配列の数を含むさらに別のクラスメンバー変数を導入します。この厳密に設定された値は、子孫クラスコンストラクタのパラメータのprotected基本オブジェクトクラスコンストラクタに必要な値を渡すことにより、各バッファオブジェクト(抽象バッファクラスの子孫)を作成するときに指定されます。

これは扱いにくく思えますが、実際にはすべてが非常に単純です。新しい指標バッファオブジェクトの作成時にはすでに、バッファオブジェクトクラスコンストラクタのいくつかの値をその親クラスコンストラクタに渡しています。さらに、もう1つの値、つまり各バッファオブジェクトの構築に必要な配列の数を渡します。

指標バッファ基本オブジェクトの抽象クラスのファイル(\MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh) に必要な変更を加えます。

クラスのprivateセクションで新しい変数を宣言します。

//+------------------------------------------------------------------+
//| Abstract indicator buffer class                                  |
//+------------------------------------------------------------------+
class CBuffer : public CBaseObj
  {
private:
   long              m_long_prop[BUFFER_PROP_INTEGER_TOTAL];                     // Integer properties
   double            m_double_prop[BUFFER_PROP_DOUBLE_TOTAL];                    // Real properties
   string            m_string_prop[BUFFER_PROP_STRING_TOTAL];                    // String properties
   bool              m_act_state_trigger;                                        // Auxiliary buffer status switch flag
   uchar             m_total_arrays;                                             // Total number of buffer arrays

protectedパラメトリッククラスコンストラクタを宣言するときは、入力にさらに別の変数を追加します。この変数は、このバッファオブジェクトの作成中に、すべてのバッファオブジェクト配列の数をクラスに渡すのに使用されます。

//--- Default constructor
                     CBuffer(void){;}
protected:
//--- Protected parametric constructor
                     CBuffer(ENUM_BUFFER_STATUS status_buffer,
                             ENUM_BUFFER_TYPE buffer_type,
                             const uint index_plot,
                             const uint index_base_array,
                             const int num_datas,
                             const uchar total_arrays,
                             const int width,
                             const string label);
public:  

コンストラクタ実装コードで、この変数を入力リストに追加し、渡された値を以前に宣言されたprivate変数に割り当てます。次に、この値を使用して、次のバッファオブジェクトの基本配列インデックスを計算しますカラー配列インデックスを計算するときは、バッファタイプを確認します。これが描画バッファの場合は、すべてのデータ配列の数を基本配列インデックスに追加してインデックスを計算します。計算バッファの場合はカラー配列がないため、ゼロを追加します

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status,
                 ENUM_BUFFER_TYPE buffer_type,
                 const uint index_plot,
                 const uint index_base_array,
                 const int num_datas,
                 const uchar total_arrays,
                 const int width,
                 const string label)
  {
   this.m_type=COLLECTION_BUFFERS_ID;
   this.m_act_state_trigger=true;
   this.m_total_arrays=total_arrays;
//--- Save integer properties
   this.m_long_prop[BUFFER_PROP_STATUS]                        = buffer_status;
   this.m_long_prop[BUFFER_PROP_TYPE]                          = buffer_type;
   ENUM_DRAW_TYPE type=
     (
      !this.TypeBuffer() || !this.Status() ? DRAW_NONE      : 
      this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING   : 
      ENUM_DRAW_TYPE(this.Status()+8)
     );
   this.m_long_prop[BUFFER_PROP_DRAW_TYPE]                     = type;
   this.m_long_prop[BUFFER_PROP_TIMEFRAME]                     = PERIOD_CURRENT;
   this.m_long_prop[BUFFER_PROP_ACTIVE]                        = true;
   this.m_long_prop[BUFFER_PROP_ARROW_CODE]                    = 0x9F;
   this.m_long_prop[BUFFER_PROP_ARROW_SHIFT]                   = 0;
   this.m_long_prop[BUFFER_PROP_DRAW_BEGIN]                    = 0;
   this.m_long_prop[BUFFER_PROP_SHOW_DATA]                     = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false);
   this.m_long_prop[BUFFER_PROP_SHIFT]                         = 0;
   this.m_long_prop[BUFFER_PROP_LINE_STYLE]                    = STYLE_SOLID;
   this.m_long_prop[BUFFER_PROP_LINE_WIDTH]                    = width;
   this.m_long_prop[BUFFER_PROP_COLOR_INDEXES]                 = (this.Status()>BUFFER_STATUS_NONE ? (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2) : 0);
   this.m_long_prop[BUFFER_PROP_COLOR]                         = clrRed;
   this.m_long_prop[BUFFER_PROP_NUM_DATAS]                     = num_datas;
   this.m_long_prop[BUFFER_PROP_INDEX_PLOT]                    = index_plot;
   this.m_long_prop[BUFFER_PROP_INDEX_BASE]                    = index_base_array;
   this.m_long_prop[BUFFER_PROP_INDEX_COLOR]                   = this.GetProperty(BUFFER_PROP_INDEX_BASE)+
                                                                   (this.TypeBuffer()!=BUFFER_TYPE_CALCULATE ? this.GetProperty(BUFFER_PROP_NUM_DATAS) : 0);
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT_BASE]               = index_base_array+this.m_total_arrays;
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT_PLOT]               = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? index_plot+1 : index_plot);
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : 0);
//--- Save string properties
   this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)]      = ::Symbol();
   this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)]       = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? label : NULL);

//--- If failed to change the size of the indicator buffer array, display the appropriate message indicating the string
   if(::ArrayResize(this.DataBuffer,(int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE)
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError());
      
//--- If failed to change the size of the color array (only for a non-calculated buffer), display the appropriate message indicating the string
   if(this.TypeBuffer()>BUFFER_TYPE_CALCULATE)
      if(::ArrayResize(this.ArrayColors,(int)this.ColorsTotal())==WRONG_VALUE)
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError());

//--- For DRAW_FILLING, fill in the color array with two default colors
   if(this.Status()==BUFFER_STATUS_FILLING)
     {
      this.SetColor(clrBlue,0);
      this.SetColor(clrRed,1);
     }

//--- Bind indicator buffers with arrays
//--- In a loop by the number of indicator buffers
   int total=::ArraySize(DataBuffer);
   for(int i=0;i<total;i++)
     {
      //--- calculate the index of the next array and
      //--- bind the indicator buffer by the calculated index with the dynamic array
      //--- located by the i loop index in the DataBuffer array
      int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i;
      ::SetIndexBuffer(index,this.DataBuffer[i].Array,(this.TypeBuffer()==BUFFER_TYPE_DATA ? INDICATOR_DATA : INDICATOR_CALCULATIONS));
      //--- Set indexation flag as in the timeseries to all buffer arrays
      ::ArraySetAsSeries(this.DataBuffer[i].Array,true);
     }
//--- Bind the color buffer with the array (only for a non-calculated buffer and not for the filling buffer)
   if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
     {
      ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorBufferArray,INDICATOR_COLOR_INDEX);
      ::ArraySetAsSeries(this.ColorBufferArray,true);
     }
//--- If this is a calculated buffer, all is done
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
//--- Set integer parameters of the graphical series
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH));
   this.SetColor((color)this.GetProperty(BUFFER_PROP_COLOR));
//--- Set real parameters of the graphical series
   ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE));
//--- Set string parameters of the graphical series
   ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL));
  }
//+------------------------------------------------------------------+


これで、抽象バッファ基本オブジェクトの子孫オブジェクトのすべてのクラスが、クラスコンストラクタの初期化リストでバッファを構築するために使用する必要のある配列の数を渡すことで補完されます

配列バッファ(\MQL5\Include\DoEasy\Objects\Indicators\BufferArrow.mqh):

//+------------------------------------------------------------------+
//| Buffer with the "Drawing with arrows" drawing style              |
//+------------------------------------------------------------------+
class CBufferArrow : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferArrow(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA,index_plot,index_base_array,1,2,1,"Arrows") {}

ラインバッファ(\MQL5\Include\DoEasy\Objects\Indicators\BufferLine.mqh):

//+------------------------------------------------------------------+
//| Buffer of the Line drawing style                                 |
//+------------------------------------------------------------------+
class CBufferLine : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferLine(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_LINE,BUFFER_TYPE_DATA,index_plot,index_base_array,1,2,1,"Line") {}

セクションバッファ(\MQL5\Include\DoEasy\Objects\Indicators\BufferSection.mqh):

//+------------------------------------------------------------------+
//| Buffer of the Section drawing style                              |
//+------------------------------------------------------------------+
class CBufferSection : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferSection(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_SECTION,BUFFER_TYPE_DATA,index_plot,index_base_array,1,2,1,"Section") {}

ゼロラインからのヒストグラムバッファ(\MQL5\Include\DoEasy\Objects\Indicators\BufferHistogram.mqh):

//+------------------------------------------------------------------+
//| Buffer of the "Histogram from the zero line" drawing style       |
//+------------------------------------------------------------------+
class CBufferHistogram : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferHistogram(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_HISTOGRAM,BUFFER_TYPE_DATA,index_plot,index_base_array,1,2,2,"Histogram") {}

2つの指標バッファのヒストグラムバッファ(\MQL5\Include\DoEasy\Objects\Indicators\BufferHistogram2.mqh):

//+--------------------------------------------------------------------+
//|Buffer of the "Histogram on two indicator buffers" drawing style    |
//+--------------------------------------------------------------------+
class CBufferHistogram2 : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferHistogram2(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_HISTOGRAM2,BUFFER_TYPE_DATA,index_plot,index_base_array,2,3,8,"Histogram2 0;Histogram2 1") {}

ジグザグバッファ(\MQL5\Include\DoEasy\Objects\Indicators\BufferZigZag.mqh):

//+------------------------------------------------------------------+
//|Buffer of the ZigZag drawing style                                |
//+------------------------------------------------------------------+
class CBufferZigZag : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferZigZag(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_ZIGZAG,BUFFER_TYPE_DATA,index_plot,index_base_array,2,3,1,"ZigZag 0;ZigZag 1") {}

塗りつぶしバッファ(\MQL5\Include\DoEasy\Objects\Indicators\BufferFilling.mqh):

//+------------------------------------------------------------------+
//|Buffer of the "Color filling between two levels" drawing style    |
//+------------------------------------------------------------------+
class CBufferFilling : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferFilling(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_FILLING,BUFFER_TYPE_DATA,index_plot,index_base_array,2,2,1,"Filling 0;Filling 1") {}

バー描画バッファ(\MQL5\Include\DoEasy\Objects\Indicators\BufferBars.mqh):

//+------------------------------------------------------------------+
//|Buffer of the Bars drawing style                                  |
//+------------------------------------------------------------------+
class CBufferBars : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferBars(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_BARS,BUFFER_TYPE_DATA,index_plot,index_base_array,4,5,2,"Bar Open;Bar High;Bar Low;Bar Close") {}

ローソク足描画バッファ(\MQL5\Include\DoEasy\Objects\Indicators\BufferCandles.mqh):

//+------------------------------------------------------------------+
//|Buffer of the Candles drawing style                               |
//+------------------------------------------------------------------+
class CBufferCandles : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferCandles(const uint index_plot,const uint index_base_array) : 
                        CBuffer(BUFFER_STATUS_CANDLES,BUFFER_TYPE_DATA,index_plot,index_base_array,4,5,1,"Candle Open;Candle High;Candle Low;Candle Close") {}

計算バッファ(\MQL5\Include\DoEasy\Objects\Indicators\BufferCalculate.mqh):

//+------------------------------------------------------------------+
//| Calculated buffer                                                |
//+------------------------------------------------------------------+
class CBufferCalculate : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferCalculate(const uint index_plot,const uint index_array) :
                        CBuffer(BUFFER_STATUS_NONE,BUFFER_TYPE_CALCULATE,index_plot,index_array,1,1,0,"Calculate") {}

このような変更により、各バッファタイプに常に同じ数の配列が使用されるため、後続の作成されたバッファのインデックスを計算するためにバッファタイプと描画スタイルのチェックを実行する必要がなくなります。この値は、バッファ作成時に、厳密に指定された形式で渡されます。

計算バッファクラスに新しいメソッドを追加して、標準の指標ハンドルから計算バッファ配列にデータを書き込みます。

//+------------------------------------------------------------------+
//| Calculated buffer                                                |
//+------------------------------------------------------------------+
class CBufferCalculate : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferCalculate(const uint index_plot,const uint index_array) :
                        CBuffer(BUFFER_STATUS_NONE,BUFFER_TYPE_CALCULATE,index_plot,index_array,1,1,0,"Calculate") {}
//--- Supported integer properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_INTEGER property);
//--- Supported real properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_DOUBLE property);
//--- Supported string properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_STRING property);
//--- Display a short buffer description in the journal
   virtual void      PrintShort(void);
   
//--- Set the value to the data buffer array
   void              SetData(const uint series_index,const double value)               { this.SetBufferValue(0,series_index,value);       }
//--- Return the value from the data buffer array
   double            GetData(const uint series_index)                            const { return this.GetDataBufferValue(0,series_index);  }
   
//--- Copy data of the specified indicator to the buffer object array
   int               FillAsSeries(const int indicator_handle,const int buffer_num,const int start_pos,const int count);
   int               FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const int count);
   int               FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const datetime stop_time);
   
  };
//+------------------------------------------------------------------+

クラス本体の外側で実装しましょう。

//+------------------------------------------------------------------+
//| Copy data of the specified indicator to the buffer object array  |
//+------------------------------------------------------------------+
int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const int start_pos,const int count)
  {
   return ::CopyBuffer(indicator_handle,buffer_num,start_pos,count,this.DataBuffer[0].Array);
  }
//+------------------------------------------------------------------+
int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const int count)
  {
   return ::CopyBuffer(indicator_handle,buffer_num,start_time,count,this.DataBuffer[0].Array);
  }
//+------------------------------------------------------------------+
int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const datetime stop_time)
  {
   return ::CopyBuffer(indicator_handle,buffer_num,start_time,stop_time,this.DataBuffer[0].Array);
  }
//+------------------------------------------------------------------+

3つのメソッドはすべて、CopyBuffer()オーバーロード関数の3つのバリアントを使用します。適切な指標バッファによって割り当てられた配列は、受信配列として使用されます。適切な指標バッファとは、必要な指標データをそのハンドルによってオブジェクト配列に書き込むためのメソッドを呼び出すバッファです。

次に、複数銘柄モードでのバッファオブジェクト操作を実装します。まず、前の記事の資料を準備するときにおこなったいくつかの仮定に対処する必要があります。この記事では、複数期間モードを実装しました。
指標バッファコレクションクラスで、単一のバッファバーを操作するために必要な時系列とバーのデータを受信するためのメソッドを作成しました。このメソッドは、チャート期間に必要なすべてのデータ(現在および割り当てられたバッファオブジェクト)と、銘柄に必要なすべてのデータ(現在および割り当てられたバッファオブジェクト)を備えています。以下は前の記事のメソッドです。

//+------------------------------------------------------------------+
//| Get data of the necessary timeseries and bars                    |
//| for working with a single bar of the buffer                      |
//+------------------------------------------------------------------+
int CBuffersCollection::GetBarsData(CBuffer *buffer,const int series_index,int &index_bar_period)
  {
//--- Get timeseries of the current chart and the chart of the buffer timeframe
   CSeriesDE *series_current=this.m_timeseries.GetSeries(buffer.Symbol(),PERIOD_CURRENT);
   CSeriesDE *series_period=this.m_timeseries.GetSeries(buffer.Symbol(),buffer.Timeframe());
   if(series_current==NULL || series_period==NULL)
      return WRONG_VALUE;
//--- Get the bar object of the current timeseries corresponding to the required timeseries index
   CBar *bar_current=series_current.GetBar(series_index);
   if(bar_current==NULL)
      return WRONG_VALUE;
//--- Get the timeseries bar object of the buffer chart period corresponding to the time the timeseries bar of the current chart falls into
   CBar *bar_period=m_timeseries.GetBarSeriesFirstFromSeriesSecond(NULL,PERIOD_CURRENT,bar_current.Time(),NULL,series_period.Timeframe());
   if(bar_period==NULL)
      return WRONG_VALUE;
//--- Write down the bar index on the current timeframe which falls into the bar start time of the buffer object chart
   index_bar_period=bar_period.Index(PERIOD_CURRENT);
//--- Calculate the amount of bars of the current timeframe included into one bar of the buffer object chart period
//--- and return this value (1 if the result is 0)
   int num_bars=::PeriodSeconds(bar_period.Timeframe())/::PeriodSeconds(bar_current.Timeframe());
   return(num_bars>0 ? num_bars : 1);
  }
//+------------------------------------------------------------------+

ここでは、必要な銘柄から2つの文字列でデータを取得できませんでした。現在のチャート時系列では、バッファオブジェクトに割り当てられた銘柄のチャートからデータを取得しました。バッファオブジェクト銘柄を取得する必要があるポイントで現在のチャート銘柄を取得する2番目の文字列では、ケースはまったく逆です。

結果的に、すべての修正は、2つのコード文字列の2つの修正のみに要約されます

以下は、修正されたメソッドの完全なコードです。

//+------------------------------------------------------------------+
//| Get data of the necessary timeseries and bars                    |
//| for working with a single bar of the buffer                      |
//+------------------------------------------------------------------+
int CBuffersCollection::GetBarsData(CBuffer *buffer,const int series_index,int &index_bar_period)
  {
//--- Get timeseries of the current chart and the chart of the buffer timeframe
   CSeriesDE *series_current=this.m_timeseries.GetSeries(Symbol(),PERIOD_CURRENT);
   CSeriesDE *series_period=this.m_timeseries.GetSeries(buffer.Symbol(),buffer.Timeframe());
   if(series_current==NULL || series_period==NULL)
      return WRONG_VALUE;
//--- Get the bar object of the current timeseries corresponding to the required timeseries index
   CBar *bar_current=series_current.GetBar(series_index);
   if(bar_current==NULL)
      return WRONG_VALUE;
//--- Get the timeseries bar object of the buffer chart period corresponding to the time the timeseries bar of the current chart falls into
   CBar *bar_period=m_timeseries.GetBarSeriesFirstFromSeriesSecond(NULL,PERIOD_CURRENT,bar_current.Time(),buffer.Symbol(),series_period.Timeframe());
   if(bar_period==NULL)
      return WRONG_VALUE;
//--- Write down the bar index on the current timeframe which falls into the bar start time of the buffer object chart
   index_bar_period=bar_period.Index(PERIOD_CURRENT);
//--- Calculate the amount of bars of the current timeframe included into one bar of the buffer object chart period
//--- and return this value (1 if the result is 0)
   int num_bars=::PeriodSeconds(bar_period.Timeframe())/::PeriodSeconds(bar_current.Timeframe());
   return(num_bars>0 ? num_bars : 1);
  }
//+------------------------------------------------------------------+

準備完了です。これで、バッファオブジェクトは複数銘柄モードでも機能できるようになりました。

現在のチャートの指定されたバーのインデックスが該当する銘柄/期間チャートのバーインデックスを返すメソッドはまだありません。このメソッドは、メイン指標ループで、現在のチャートに別の銘柄/期間からのデータを正しく表示するために必要です。

このようなメソッドに最適な場所は、時系列コレクションクラスの \MQL5\Include\DoEasy\Collections\TimeSeriesCollection.mqhです。
ここに新しいメソッドを宣言します

//--- Return the bar object of the specified timeseries of the specified symbol of the specified position (1) by index, (2) by time
//--- bar object of the first timeseries corresponding to the bar open time on the second timeseries (3) by index, (4) by time
   CBar                   *GetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const bool from_series=true);
   CBar                   *GetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime bar_time);
   CBar                   *GetBarSeriesFirstFromSeriesSecond(const string symbol_first,const ENUM_TIMEFRAMES timeframe_first,const int index,
                                                             const string symbol_second=NULL,const ENUM_TIMEFRAMES timeframe_second=PERIOD_CURRENT);
   CBar                   *GetBarSeriesFirstFromSeriesSecond(const string symbol_first,const ENUM_TIMEFRAMES timeframe_first,const datetime first_bar_time,
                                                             const string symbol_second=NULL,const ENUM_TIMEFRAMES timeframe_second=PERIOD_CURRENT);

//--- Return the bar index on the specified timeframe chart by the current chart's bar index                                |
   int                     IndexBarPeriodByBarCurrent(const int series_index,const string symbol,const ENUM_TIMEFRAMES timeframe);

//--- Return the flag of opening a new bar of the specified timeseries of the specified symbol
   bool                    IsNewBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time=0);

クラス本体の外側で実装しましょう。

//+------------------------------------------------------------------+
//| Return the bar index on the specified timeframe chart            |
//| by the current chart's bar index                                 |
//+------------------------------------------------------------------+
int CTimeSeriesCollection::IndexBarPeriodByBarCurrent(const int series_index,const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
   CSeriesDE *series=this.GetSeries(::Symbol(),(ENUM_TIMEFRAMES)::Period());
   if(series==NULL)
      return WRONG_VALUE;
   CBar *bar=series.GetBar(series_index);
   if(bar==NULL)
      return WRONG_VALUE;
   return ::iBarShift(symbol,timeframe,bar.Time());
  }
//+------------------------------------------------------------------+

このメソッドは、現在のチャートバーインデックスと、チャートの銘柄および期間を受け取ります。メソッドに渡された現在のチャートインデックスの時間に対応するバーインデックスを返す必要があります。

さらに、現在のチャート時系列へのポインタを取得し、現在の時系列インデックスによってバーオブジェクトへのポインタを取得します。また、バー時間を使用して、必要な時系列の対応するバーのインデックスを返します

計算バッファは、指標が基づいている時系列に従って指標バッファのデータを完全に格納するため、このメソッドを使用して、現在のチャートで指定されたバーインデックスに対応する計算バッファ配列のインデックスを取得します( ここでは、指標ループインデックスを実際の例として使用できます)。2つのさまざまな時系列の間でこのような一致を確立できれば、これらのデータを必要なチャートに正しく表示できます。

カスタムプログラムからメソッドにアクセスするには、CEngineライブラリのメインオブジェクトクラス(\MQL5\Include\DoEasy\Engine.mqh)からメソッドへのアクセスを提供する必要があります。

//--- Clear data by the timeseries index for the (1) arrow, (2) line, (3) section, (4) zero line histogram,
//--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles
   void                 BufferArrowClear(const int number,const int series_index)         { this.m_buffers.ClearBufferArrow(number,series_index);     }
   void                 BufferLineClear(const int number,const int series_index)          { this.m_buffers.ClearBufferLine(number,series_index);      }
   void                 BufferSectionClear(const int number,const int series_index)       { this.m_buffers.ClearBufferSection(number,series_index);   }
   void                 BufferHistogramClear(const int number,const int series_index)     { this.m_buffers.ClearBufferHistogram(number,series_index); }
   void                 BufferHistogram2Clear(const int number,const int series_index)    { this.m_buffers.ClearBufferHistogram2(number,series_index);}
   void                 BufferZigZagClear(const int number,const int series_index)        { this.m_buffers.ClearBufferZigZag(number,series_index);    }
   void                 BufferFillingClear(const int number,const int series_index)       { this.m_buffers.ClearBufferFilling(number,series_index);   }
   void                 BufferBarsClear(const int number,const int series_index)          { this.m_buffers.ClearBufferBars(number,series_index);      }
   void                 BufferCandlesClear(const int number,const int series_index)       { this.m_buffers.ClearBufferCandles(number,series_index);   }

//--- Return the bar index on the specified timeframe chart by the current chart's bar index
   int                  IndexBarPeriodByBarCurrent(const int series_index,const string symbol,const ENUM_TIMEFRAMES timeframe)
                          { return this.m_time_series.IndexBarPeriodByBarCurrent(series_index,symbol,timeframe);  }

//--- Display short description of all indicator buffers of the buffer collection
   void                 BuffersPrintShort(void);

これで、複数銘柄・複数期間指標の開発と処理をテストするためのライブラリクラスの改善が完了しました

テストを実行するには、移動平均とMACDの2つの複数銘柄・複数期間指標を作成して、現在のチャートの指定された銘柄/期間から取得したデータを描画します。指標設定で、標準指標データの取得元となる指標のパラメータとチャート期間/銘柄を設定します。

テスト: 複数銘柄・複数期間移動平均

テストを実行するには、前の記事のテスト指標を、\MQL5\Indicators\TestDoEasy\Part45\TestDoEasyPart46.mq5として保存します。

指標は、設定で指定された銘柄/期間のローソク足を別のサブウィンドウに表示します。指定されたパラメータと同じ銘柄/期間の移動平均が同じサブウィンドウに表示されます。

チャートサブウィンドウで指標データを表示を設定し、指標の銘柄値とチャート期間を入力し、移動平均入力を設定します。また、入力されたMAパラメータを調整するためのグローバル変数を設定します

//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- properties
#property indicator_separate_window
#property indicator_buffers 8
#property indicator_plots   2

//--- classes

//--- enums

//--- defines

//--- structures

//--- input variables
sinput   string               InpUsedSymbols    =  "GBPUSD";      // Used symbol (one only)
sinput   ENUM_TIMEFRAMES      InpPeriod         =  PERIOD_M30;    // Used chart period
//---
sinput   uint                 InpPeriodMA       =  14;            // MA Period
sinput   int                  InpShiftMA        =  0;             // MA Shift
sinput   ENUM_MA_METHOD       InpMethodMA       =  MODE_SMA;      // MA Method
sinput   ENUM_APPLIED_PRICE   InpPriceMA        =  PRICE_CLOSE;   // MA Applied Price
//---
sinput   bool                 InpUseSounds      =  true;          // Use sounds

//--- indicator buffers
CArrayObj     *list_buffers;                                      // Pointer to the buffer object list
//--- global variables
ENUM_SYMBOLS_MODE    InpModeUsedSymbols=  SYMBOLS_MODE_DEFINES;   // Mode of used symbols list
ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_LIST;   // Mode of used timeframes list
string               InpUsedTFs;                                  // List of used timeframes
CEngine              engine;                                      // CEngine library main object
string               prefix;                                      // Prefix of graphical object names
int                  min_bars;                                    // The minimum number of bars for the indicator calculation
int                  used_symbols_mode;                           // Mode of working with symbols
string               array_used_symbols[];                        // The array for passing used symbols to the library
string               array_used_periods[];                        // The array for passing used timeframes to the library
int                  handle_ma;                                   // МА handle
int                  period_ma;                                   // Moving Average calculation period
//+------------------------------------------------------------------+

OnInit()ハンドラーで、3つのバッファオブジェクトを作成します。最初のオブジェクトはМАライン、2番目のオブジェクトは選択した銘柄ローソク足、3番目は選択した銘柄/期間から取得した移動平均データを格納するために計算されたものです。
次に、ローソク足バッファの4つのデータウィンドウバッファ配列すべての説明を設定します。また、MAラインバッファに対しても同じことを実行します。完了したら、移動平均指標ハンドルを作成します

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Write the name of the working timeframe selected in the settings to the InpUsedTFs variable
   InpUsedTFs=TimeframeDescription(InpPeriod);
//--- Initialize DoEasy library
   OnInitDoEasy();
   IndicatorSetInteger(INDICATOR_DIGITS,(int)SymbolInfoInteger(InpUsedSymbols,SYMBOL_DIGITS)+1);
//--- Set indicator global variables
   prefix=engine.Name()+"_";
   //--- Get the index of the maximum used timeframe in the array,
   //--- calculate the number of bars of the current period fitting in the maximum used period
   //--- Use the obtained value if it exceeds 2, otherwise use 2
   int index=ArrayMaximum(ArrayUsedTimeframes);
   int num_bars=NumberBarsInTimeframe(ArrayUsedTimeframes[index]);
   min_bars=(index>WRONG_VALUE ? (num_bars>2 ? num_bars : 2) : 2);

//--- Check and remove remaining indicator graphical objects
   if(IsPresentObectByPrefix(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel

//--- Check playing a standard sound using macro substitutions
   engine.PlaySoundByDescription(SND_OK);
//--- Wait for 600 milliseconds
   engine.Pause(600);
   engine.PlaySoundByDescription(SND_NEWS);

//--- indicator buffers mapping
//--- Create all the necessary buffer objects
   engine.BufferCreateLine();          // 2 arrays
   engine.BufferCreateCandles();       // 5 arrays
   engine.BufferCreateCalculate();     // 1 array

//--- Check the number of buffers specified in the 'properties' block
   if(engine.BuffersPropertyPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal());
   if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
      
//--- Create the color array and set non-default colors to all buffers within the collection
   color array_colors[]={clrDodgerBlue,clrRed,clrGray};
   engine.BuffersSetColors(array_colors);
    
//--- Set МА period
   period_ma=int(InpPeriodMA<2 ? 2 : InpPeriodMA);

//--- In a loop by the list of collection buffer objects,
   for(int i=0;i<engine.GetListBuffers().Total();i++)
     { 
      //--- get the next buffer
      CBuffer *buff=engine.GetListBuffers().At(i);
      if(buff==NULL)
         continue;
      //--- and set its display in the data window depending on its specified usage
      //--- and also a chart period and symbol selected in the settings
      buff.SetShowData(true);
      buff.SetTimeframe(InpPeriod);
      buff.SetSymbol(InpUsedSymbols);
      if(buff.Status()==BUFFER_STATUS_CANDLES)
        {
         string pr=InpUsedSymbols+" "+TimeframeDescription(InpPeriod)+" ";
         string label=pr+"Open;"+pr+"High;"+pr+"Low;"+pr+"Close";
         buff.SetLabel(label);
        }
      if(buff.Status()==BUFFER_STATUS_LINE)
        {
         string label="MA("+(string)period_ma+")";
         buff.SetLabel(label);
        }
     }
     
//--- Display short descriptions of created indicator buffers
   engine.BuffersPrintShort();

//--- Create МА handle
   handle_ma=iMA(InpUsedSymbols,InpPeriod,period_ma,InpShiftMA,InpMethodMA,InpPriceMA);
   if(handle_ma==INVALID_HANDLE)
      return INIT_FAILED;
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

OnCalculate()ハンドラーのデータ準備ブロックで、MAデータを計算バッファにコピーします。

各ティックでMAデータ配列全体がコピーされないようにするには、「limit」変数が最初の起動時または履歴データの変更時に1を超えるように計算され、新しいバーが開いた場合は1に等しく、それ以外では各ティックで0であるよう注意する必要があります。
ゼロバーをコピーすることはできないので、「limit」値に基づいてデータをコピーすることはできません。つまり、「limit」がゼロに等しい場合はバーを1つコピーし、それ以外の場合は、「limit」で指定された数だけコピーします。これで、関連するMAデータを計算バッファにコピーするのに、リソースを節約できます。

//--- Prepare data
   CBufferCalculate *buff_calc=engine.GetBufferCalculate(0);
   int total_copy=(limit<2 ? 1 : limit);
   int copied=buff_calc.FillAsSeries(handle_ma,0,0,total_copy);
   if(copied<total_copy)
      return 0;
        

メイン指標ループで、最初に描画されたすべての指標バッファの現在のバーを消去し(不要な値を取り除くため)、次に選択した銘柄のMAラインとローソク足を再計算しながら現在のチャートでの表示を再計算します

//--- Main calculation loop of the indicator
   for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
     {
      //--- Clear the current bar of all created buffers
      engine.BufferLineClear(0,0);
      engine.BufferCandlesClear(0,0);
      
      //--- Get the timeseries bar corresponding to the loop index time on the chart period specified in the settings
      bar=engine.SeriesGetBar(InpUsedSymbols,InpPeriod,time[i]);
      if(bar==NULL)
         continue;
      //--- Calculate the color index depending on the candle direction on the timeframe specified in the settings
      color_index=(bar.TypeBody()==BAR_BODY_TYPE_BULLISH ? 0 : bar.TypeBody()==BAR_BODY_TYPE_BEARISH ? 1 : 2);
      //--- Calculate the MA line buffer
      int index=engine.IndexBarPeriodByBarCurrent(i,InpUsedSymbols,InpPeriod);
      if(index<0)
         continue;
      engine.BufferSetDataLine(0,i,buff_calc.GetData(index),color_index);
      //--- Calculate the candle buffer
      engine.BufferSetDataCandles(0,i,bar.Open(),bar.High(),bar.Low(),bar.Close(),color_index);
     }
//--- return value of prev_calculated for next call

完全な指標コードは、以下に添付されているファイルで提供されます。

GBPUSD M30と、期間が14でシフトが0の単純な終値による移動平均を指定してEURUSD M15で指標を起動します。


比較のために、同じパラメータを持つ移動平均指標を使用したGBPUSDチャートを開きました。


テスト: 複数銘柄・複数期間MACD

それでは、複数銘柄・複数期間MACDを作成しましょう。新しく作成した指標をTestDoEasyPart46_2.mq5として保存します。

MACDの入力と、その計算と表示に必要なすべてのバッファを設定します。現在のチャートにMACDを表示するための2つ(ヒストグラムとライン)の描画バッファ、および設定で指定された銘柄/期間から取得したヒストグラムデータとMACDシグナル線を格納するための2つの計算バッファが必要になります。

すべてのアクションとロジックの詳細はコードコメントで説明してみたので、ここでは前の指標と比較した基本的な変更についてのみ説明します。

//+------------------------------------------------------------------+
//|                                           TestDoEasyPart46_2.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- properties
#property indicator_separate_window
#property indicator_buffers 6
#property indicator_plots   2

//--- classes

//--- enums

//--- defines

//--- structures

//--- input variables
sinput   string               InpUsedSymbols    =  "GBPUSD";      // Used symbol (one only)
sinput   ENUM_TIMEFRAMES      InpPeriod         =  PERIOD_M30;    // Used chart period
//---
sinput   uint                 InpPeriodFastEMA  =  12;            // MACD Fast EMA Period
sinput   uint                 InpPeriodSlowEMA  =  26;            // MACD Slow EMA Period
sinput   uint                 InpPeriodSignalMA =  9;             // MACD Signal MA Period
sinput   ENUM_APPLIED_PRICE   InpPriceMACD      =  PRICE_CLOSE;   // MA Applied Price
//---
sinput   bool                 InpUseSounds      =  true;          // Use sounds
//--- indicator buffers
CArrayObj     *list_buffers;                                      // Pointer to the buffer object list
//--- global variables
ENUM_SYMBOLS_MODE    InpModeUsedSymbols=  SYMBOLS_MODE_DEFINES;   // Mode of used symbols list
ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_LIST;   // Mode of used timeframes list
string               InpUsedTFs;                                  // List of used timeframes
CEngine              engine;                                      // CEngine library main object
string               prefix;                                      // Prefix of graphical object names
int                  min_bars;                                    // The minimum number of bars for the indicator calculation
int                  used_symbols_mode;                           // Mode of working with symbols
string               array_used_symbols[];                        // The array for passing used symbols to the library
string               array_used_periods[];                        // The array for passing used timeframes to the library
int                  handle_macd;                                 // МАCD handle
int                  fast_ema_period;                             // Fast EMA calculation period
int                  slow_ema_period;                             // Slow EMA calculation period
int                  signal_period;                               // MACD signal line calculation period
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Write the name of the working timeframe selected in the settings to the InpUsedTFs variable
   InpUsedTFs=TimeframeDescription(InpPeriod);
//--- Initialize DoEasy library
   OnInitDoEasy();
   IndicatorSetInteger(INDICATOR_DIGITS,(int)SymbolInfoInteger(InpUsedSymbols,SYMBOL_DIGITS)+1);
//--- Set indicator global variables
   prefix=engine.Name()+"_";
   //--- calculate the number of bars of the current period fitting in the maximum used period
   //--- Use the obtained value if it exceeds 2, otherwise use 2
   int num_bars=NumberBarsInTimeframe(InpPeriod);
   min_bars=(num_bars>2 ? num_bars : 2);

//--- Check and remove remaining indicator graphical objects
   if(IsPresentObectByPrefix(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel

//--- Check playing a standard sound using macro substitutions
   engine.PlaySoundByDescription(SND_OK);
//--- Wait for 600 milliseconds
   engine.Pause(600);
   engine.PlaySoundByDescription(SND_NEWS);

//--- indicator buffers mapping
//--- Create all the necessary buffer objects for constructing MACD
   engine.BufferCreateHistogram();     // 2 arrays
   engine.BufferCreateLine();          // 2 arrays
   engine.BufferCreateCalculate();     // 1 array for MACD histogram data from the specified symbol/period
   engine.BufferCreateCalculate();     // 1 array for MACD signal line from the specified symbol/period

//--- Check the number of buffers specified in the 'properties' block
   if(engine.BuffersPropertyPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal());
   if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
      
//--- Create the color array and set non-default colors to all buffers within the collection
   color array_colors[]={clrDodgerBlue,clrRed,clrGray};
   engine.BuffersSetColors(array_colors);

//--- Set МАCD calculation periods
   fast_ema_period=int(InpPeriodFastEMA<1 ? 1 : InpPeriodFastEMA);
   slow_ema_period=int(InpPeriodSlowEMA<1 ? 1 : InpPeriodSlowEMA);
   signal_period=int(InpPeriodSignalMA<1  ? 1 : InpPeriodSignalMA);

//--- Get the histogram buffer (the first drawn buffer)
//--- It has the index of 0 considering that the starting point is zero
   CBufferHistogram *buff_hist=engine.GetBufferHistogram(0);
   if(buff_hist!=NULL)
     {
      //--- Set the line width for the histogram
      buff_hist.SetWidth(3);
      //--- Set the graphical series description for the histogram
      string label="MACD ("+(string)fast_ema_period+","+(string)slow_ema_period+","+(string)signal_period+")";
      buff_hist.SetLabel(label);
      //--- and set display in the data window for the buffer
      //--- and also a chart period and symbol selected in the settings
      buff_hist.SetShowData(true);
      buff_hist.SetTimeframe(InpPeriod);
      buff_hist.SetSymbol(InpUsedSymbols);
     }
//--- Get the signal line buffer (the first drawn buffer)
//--- It has the index of 0 considering that the starting point is zero
   CBufferLine *buff_line=engine.GetBufferLine(0);
   if(buff_line!=NULL)
     {
      //--- Set the signal line width
      buff_line.SetWidth(1);
      //--- Set the graphical series description for the signal line
      string label="Signal";
      buff_line.SetLabel(label);
      //--- and set display in the data window for the buffer
      //--- and also a chart period and symbol selected in the settings
      buff_line.SetShowData(true);
      buff_line.SetTimeframe(InpPeriod);
      buff_line.SetSymbol(InpUsedSymbols);
     }
    
//--- Get the first calculated buffer
//--- It has the index of 0 considering that the starting point is zero
   CBufferCalculate *buff_calc=engine.GetBufferCalculate(0);
   if(buff_calc!=NULL)
     {
      //--- Set the description of the first calculated buffer as the "MACD histogram temporary array""
      buff_calc.SetLabel("MACD_HIST_TMP");
      //--- and set a chart period and symbol selected in the settings for it
      buff_calc.SetTimeframe(InpPeriod);
      buff_calc.SetSymbol(InpUsedSymbols);
     }
//--- Get the second calculated buffer
//--- It has the index of 1 considering that the starting point is zero
   buff_calc=engine.GetBufferCalculate(1);
   if(buff_calc!=NULL)
     {
      //--- Set the description of the second calculated buffer as the "MACD signal line temporary array""
      buff_calc.SetLabel("MACD_SIGN_TMP");
      //--- and set a chart period and symbol selected in the settings for it
      buff_calc.SetTimeframe(InpPeriod);
      buff_calc.SetSymbol(InpUsedSymbols);
     }
    
//--- Display short descriptions of created indicator buffers
   engine.BuffersPrintShort();

//--- Create МАCD handle
   handle_macd=iMACD(InpUsedSymbols,InpPeriod,fast_ema_period,slow_ema_period,signal_period,InpPriceMACD);
   if(handle_macd==INVALID_HANDLE)
      return INIT_FAILED;
//---
   IndicatorSetString(INDICATOR_SHORTNAME,InpUsedSymbols+" "+TimeframeDescription(InpPeriod)+" MACD("+(string)fast_ema_period+","+(string)slow_ema_period+","+(string)signal_period+")");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Remove indicator graphical objects by an object name prefix
   ObjectsDeleteAll(0,prefix);
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//+------------------------------------------------------------------+
//| OnCalculate code block for working with the library:             |
//+------------------------------------------------------------------+
//--- Pass the current symbol data from OnCalculate() to the price structure and set the "as timeseries" flag to the arrays
   CopyDataAsSeries(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread);

//--- Check for the minimum number of bars for calculation
   if(rates_total<min_bars || Point()==0) return 0;
   
//--- Handle the Calculate event in the library
//--- If the OnCalculate() method of the library returns zero, not all timeseries are ready - leave till the next tick
   if(engine.0)
      return 0;
   
//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER)) 
     {
      engine.OnTimer(rates_data);   // Working in the library timer
      EventsHandling();             // Working with library events
     }
//+------------------------------------------------------------------+
//| OnCalculate code block for working with the indicator:           |
//+------------------------------------------------------------------+
//--- Check and calculate the number of calculated bars
//--- If limit = 0, there are no new bars - calculate the current one
//--- If limit = 1, a new bar has appeared - calculate the first and the current ones
//--- limit > 1 means the first launch or changes in history - the full recalculation of all data
   int limit=rates_total-prev_calculated;
   
//--- Recalculate the entire history
   if(limit>1)
     {
      limit=rates_total-1;
      engine.BuffersInitPlots();
      engine.BuffersInitCalculates();
     }

//--- Prepare data 
   int total_copy=(limit<2 ? 1 : limit);
//--- Get the first calculated buffer by its number
   CBufferCalculate *buff_calc_hist=engine.GetBufferCalculate(0);
//--- Fill in the first calculated buffer with MACD histogram data
   int copied=buff_calc_hist.FillAsSeries(handle_macd,0,0,total_copy);
   if(copied<total_copy)
      return 0;
//--- Get the second calculated buffer by its number
   CBufferCalculate *buff_calc_sig=engine.GetBufferCalculate(1);
//--- Fill in the second calculated buffer with MACD signal line data
   copied=buff_calc_sig.FillAsSeries(handle_macd,1,0,total_copy);
   if(copied<total_copy)
      return 0;
        
//--- Calculate the indicator
   CBar *bar=NULL;         // Bar object for defining the candle direction
   uchar color_index=0;    // Color index to be set for the buffer depending on the candle direction

//--- Main calculation loop of the indicator
   for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
     {
      //--- Clear the current bar of all created buffers
      engine.BufferHistogramClear(0,0);
      engine.BufferLineClear(0,0);
      
      //--- Get the timeseries bar corresponding to the loop index time on the chart period specified in the settings
      bar=engine.SeriesGetBar(InpUsedSymbols,InpPeriod,time[i]);
      if(bar==NULL)
         continue;
      //--- Calculate the color index depending on the candle direction on the timeframe specified in the settings
      color_index=(bar.TypeBody()==BAR_BODY_TYPE_BULLISH ? 0 : bar.TypeBody()==BAR_BODY_TYPE_BEARISH ? 1 : 2);
      //--- Calculate the MACD histogram buffer
      int index=engine.IndexBarPeriodByBarCurrent(i,InpUsedSymbols,InpPeriod);
      if(index<0)
         continue;
      engine.BufferSetDataHistogram(0,i,buff_calc_hist.GetData(index),color_index);
      //--- Calculate MACD signal line buffer
      engine.BufferSetDataLine(0,i,buff_calc_sig.GetData(index),color_index);
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

各バーのヒストグラムとシグナル線の列の色は、MACDが基づく銘柄/期間ローソク足の方向に対応しています。


指標は、GBPUSDM30に基づくMACDのデフォルト設定でEURUSDM15で起動されます。より明確にするために、同じパラメータを持つ標準MACDを使用したGBPUSDM30チャートを開きます。

次の段階

次の記事では、複数銘柄・複数期間標準指標の作成を容易にする機能をライブラリに追加します。

現在のバージョンのライブラリのすべてのファイルは、テスト用EAファイルと一緒に以下に添付されているので、テストするにはダウンロードしてください。
質問、コメント、提案はコメント欄にお願いします。テスト指標はMQL5向けに開発されたのでご注意ください
添付ファイルはMetaTrader 5のみを対象としています。現在のライブラリバージョンはMetaTrader 4ではテストされていません。
指標バッファ操作を開発してテストした後で、MetaTrader 4にいくつかのMQL5機能を実装してみます。

目次に戻る

シリーズのこれまでの記事:

DoEasyライブラリの時系列(第35部): バーオブジェクトと銘柄の時系列リスト
DoEasyライブラリの時系列(第36部): すべての使用銘柄期間の時系列オブジェクト
DoEasyライブラリの時系列(第37部): すべての使用銘柄期間の時系列オブジェクト
DoEasyライブラリの時系列(第38部): 時系列コレクション-リアルタイムの更新とプログラムからのデータへのアクセス
DoEasyライブラリの時系列(第39部): ライブラリに基づいた指標 - データイベントと時系列イベントの準備
DoEasyライブラリの時系列(第40部): ライブラリに基づいた指標 - 実時間でのデータ更新
DoEasyライブラリの時系列(第41部): 複数銘柄・複数期間指標の例
DoEasyライブラリの時系列(第42部): 抽象指標バッファオブジェクトクラス
DoEasyライブラリの時系列(第43部): 指標バッファオブジェクトクラス
DoEasyライブラリの時系列(第44部): 指標バッファオブジェクトのコレクションクラス
DoEasyライブラリの時系列(第45部): 複数期間指標バッファ

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/8115

添付されたファイル |
MQL5.zip (3753.87 KB)
クイック手動取引ツールキット: ポジションと指値注文の使用 クイック手動取引ツールキット: ポジションと指値注文の使用
本稿では、ツールキットの機能を拡張します。特定の条件で取引ポジションを決済する機能を追加し、これらの注文を編集する機能を備えた、成行注文と指値注文を制御するための表を作成します。
数式の計算(第2部)Prattパーサーおよび操車場パーサー 数式の計算(第2部)Prattパーサーおよび操車場パーサー
この記事では、演算子の優先順位に基づいたパーサーを使用した数式の解析と評価の原則について検討します。Prattパーサーと操車場パーサー、バイトコードの生成とこのコードによる計算を実装し、式の関数として指標を使用する方法と、これらの指標に基づいてエキスパートアドバイザーで取引シグナルを設定する方法を確認します。
確率論と数理統計学と例(第1部): 基礎理論と初等理論 確率論と数理統計学と例(第1部): 基礎理論と初等理論
取引とは、常に不確実性に直面して意思決定を行うことです。つまり、これらの決定が行われた時点では、決定の結果が明確ではありません。これには、そのようなケースを意味ある方法で説明できるようにする数学的モデルの構築への理論的アプローチの重要性が必然的に伴います。
DoEasyライブラリの時系列(第45部): 複数期間指標バッファ DoEasyライブラリの時系列(第45部): 複数期間指標バッファ
本稿では、複数期間モードと複数銘柄モードで使用する指標バッファオブジェクトおよびコレクションクラスの改善を始めます。現在の銘柄チャートの任意の時間枠からデータを受信して表示するためのバッファオブジェクトの使用を検討するつもりです。