English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
マルチタイムフレームとマルチ通貨パネルの作成

マルチタイムフレームとマルチ通貨パネルの作成

MetaTrader 5 | 2 10月 2015, 15:59
1 389 0
Marcin Konieczny
Marcin Konieczny

はじめに

この記事では、オブジェクト指向プログラミングを使うMetaTrader 5のマルチタイムフレームとマルチ通貨パネルの作成の仕方を解説します。主な目的は、パネルのコードを変更することなく、価格や価格の変化、インディケーターの値や売買条件のカスタマイズなど多くの異なった種類のデータを表示することのできる汎用パネルの作成です。そうすることで、パネルを必要とするいろいろな方法でカスタマイズしたい時に必要となるコーディングをわずかにすることができます。

これから解説するソリューションは2つのモードで動きます:

  1. マルチタイムフレームモード - 現在のシンボルを異なるタイムフレームで表示するタイムテーブルで見ることができます。
  2. マルチ通貨モード - 異なるシンボルを現在のタイムフレームで表示するテーブルを見ることができます。

以下の図はそれぞれのモードでのパネルを表しています。

1つ目はマルチタイムフレームモードで動き、次のデータを表示します:

  1. 現在の価格;
  2. 現在の足の価格の変化;
  3. 現在の足の価格の変化率;
  4. 現在の足の価格の変化の矢印(上昇/下落);
  5. RSI(14)インディケーターの値;
  6. RSI(10)インディケーターの値;
  7. カスタムコンディション:SMA(20) >現在の価格。

図 1. マルチタイムフレームモード

図 1. マルチタイムフレームモード

2つ目はマルチ通貨モードで動き、次のデータを表示します:

  1. 現在の価格;
  2. 現在の足の価格の変化;
  3. 現在の足の価格の変化率;
  4. 現在の足の価格の変化の矢印(上昇/下落);
  5. RSI(10)インディケーターの値;
  6. RSI(14)インディケーターの値;
  7. カスタムコンディション:SMA(20) >現在の価格。

図 2. マルチ通貨モード

図 2. マルチ通貨モード


1. インプリメンテーション

以下のクラス図はパネルのインプリメンテーションデザインを表しています。

図 3. パネルのクラス図

図 3. パネルのクラス図

図のそれぞれの要素を説明します:

  1. CTable. パネルのコアクラス。パネルの描画とそのコンポーネントの管理を担当します。
  2. SpyAgent.シンボル(投資対象)を”スパイする”インディケーターを管理します。それぞれのAgentは異なるシンボルに対し作成され送信されます。Agentは通貨ペアチャート上に新しいティックがある度にOnCalculateイベントに反応しCHARTEVENT_CUSTOM イベントをCTableオブジェクトに対し送信し、更新します。このアプローチに対するすべての考え方は記事、"The Implementation of a Multi-currency Mode in MetaTrader 5""(MetaTrader 5のマルチ通貨モードのインプリメンテーション)に基づいています。テクニカルな詳細はそこで見ることができます。
  3. CRow. パネルを作るためのすべてのインディケーターとコンディションの親クラス。このクラスを拡張することで、パネルに必要なすべてのコンポーネントを作成することが可能です。
  4. CPriceRow. CRowを拡張するシンプルなクラス、現在のビッドプライスを表示するのに使われます。
  5. CPriceChangeRow. CRowを拡張するクラス、現在の足のプライスの変化を表示するのに使われます。プライスの変化、変化率や矢印を表示することができます。
  6. CRSIRow. CRowを拡張するクラス、現在のRSIインディケーターの値を表示するのに使われます。
  7. CPriceMARow. CRowを拡張するクラス、カスタムコンディションを表示します:SMA >現在のプライス。

CTableとCRow のクラスに加え、SpyAgentインディケーターもパネルの核となるパーツです。CPriceRow、 CPriceChangeRow、 CRSIRowやCPriceMARowはパネルの実際の中身です。CRowクラスは必要な結果を得るために多くの新しいクラスにより拡張されるようにデザインされています。紹介された4つの派生クラスは何ができるか、またどのように行うのかを示した簡単な例です。


2. SpyAgent

まずは、SpyAgentインディケーターから始めましょう。マルチ通貨モードでのみ使われ、他のチャートに新しいティックがあった時にパネルを更新するのに必要です。これに関してはあまり深くは説明しません。それらは記事"The Implementation of a Multi-currency Mode in MetaTrader 5"(MetaTrader 5のマルチ通貨モードのインプリメンテーション)で詳しく説明しています。

SpyAgentインディケーターは指定されたシンボルのチャート上で実行され2つのイベントを送信します:初期化イベントと新しいティックイベントです。両方のイベントはCHARTEVENT_CUSTOMタイプです。これらのイベントを処理するためにOnChartEvent(...)ハンドラ(この記事の後半で説明します)を使います。

SpyAgentのコードを見てみましょう:

//+------------------------------------------------------------------+
//|                                                     SpyAgent.mq5 |
//|                                                 Marcin Konieczny |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Marcin Konieczny"
#property indicator_chart_window
#property indicator_plots 0

input long   chart_id=0;        // chart id
input ushort custom_event_id=0; // event id
//+------------------------------------------------------------------+
//| Indicator iteration function                                     |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {

   if(prev_calculated==0)
      EventChartCustom(chart_id,0,0,0.0,_Symbol); // sends initialization event
   else
      EventChartCustom(chart_id,(ushort)(custom_event_id+1),0,0.0,_Symbol); // sends new tick event

   return(rates_total);
  }

かなりシンプルです。. このコードが唯一行うのは新しいティックを受け取りCHARTEVENT_CUSTOMイベントを送信することだけです。


3. CTable

CTableはパネルのコアクラスです。パネル設定についての情報を格納し、そのコンポーネントの管理をします。必要であれば、パネルを更新(再描画)します。

CTableの宣言について見てみましょう:

//+------------------------------------------------------------------+
//| CTable class                                                     |
//+------------------------------------------------------------------+
class CTable
  {
private:
   int               xDistance;    // distance from right border of the chart
   int               yDistance;    // distance from top of the chart
   int               cellHeight;   // table cell height
   int               cellWidth;    // table cell width
   string            font;         // font name
   int               fontSize;
   color             fontColor;

   CList            *rowList;      // list of row objects
   bool              tfMode;       // is in multi-timeframe mode?

   ENUM_TIMEFRAMES   timeframes[]; // array of timeframes for multi-timeframe mode
   string            symbols[];    // array of currency pairs for multi-currency mode

   //--- private methods
   //--- sets default parameters of the table
   void              Init();
   //--- draws text label in the specified table cell
   void              DrawLabel(int x,int y,string text,string font,color col);
   //--- returns textual representation of given timeframe
   string            PeriodToString(ENUM_TIMEFRAMES period);

public:
   //--- multi-timeframe mode constructor
                     CTable(ENUM_TIMEFRAMES &tfs[]);
   //--- multi-currency mode constructor
                     CTable(string &symb[]);
   //--- destructor
                    ~CTable();
   //--- redraws table
   void              Update();
   //--- methods for setting table parameters
   void              SetDistance(int xDist,int yDist);
   void              SetCellSize(int cellW,int cellH);
   void              SetFont(string fnt,int size,color clr);
   //--- appends CRow object to the of the table
   void              AddRow(CRow *row);
  };

ご覧の通り、全てのパネルのコンポーネント(行)はCRowのポインタのリストとして格納されるので、パネルに追加したいコンポーネントはCRowクラスを拡張する必要があります。CRowはパネルとそのコンポーネントのコントラクトと見ることができます。CTableにはそのセルを計算するためのいかなるコードも含まれません。それはCRowを拡張するクラスの役割です。CTableはCRowのコンポーネントを保持し必要な際に再描画するためのストラクチャーでしかありません。

CTableのメソッドについて見てみましょう。このクラスには2つのコンストラクタがあります。1つ目はマルチタイムフレームモードで動き、とてもシンプルです。表示したいタイムフレームの配列を作るだけです。

//+------------------------------------------------------------------+
//| Multi-timeframe mode constructor                                 |
//+------------------------------------------------------------------+
CTable::CTable(ENUM_TIMEFRAMES &tfs[])
  {
//--- copy all timeframes to own array
   ArrayResize(timeframes,ArraySize(tfs),0);
   ArrayCopy(timeframes,tfs);
   tfMode=true;
   
//--- fill symbols array with current chart symbol
   ArrayResize(symbols,ArraySize(tfs),0);
   for(int i=0; i<ArraySize(tfs); i++)
      symbols[i]=Symbol();

//--- set default parameters
   Init();
  }

2つ目はマルチ通貨モードで使用され、シンボル(投資対象)の配列を受け取ります。このコンストラクタもSpyAgentを送信します。1つずつ適切なチャートに貼り付けます。

//+------------------------------------------------------------------+
//| Multi-currency mode constructor                                  |
//+------------------------------------------------------------------+
CTable::CTable(string &symb[])
  {
//--- copy all symbols to own array
   ArrayResize(symbols,ArraySize(symb),0);
   ArrayCopy(symbols,symb);
   tfMode=false;
   
//--- fill timeframe array with current timeframe
   ArrayResize(timeframes,ArraySize(symb),0);
   ArrayInitialize(timeframes,Period());

//--- set default parameters
   Init();

//--- send SpyAgents to every requested symbol
   for(int x=0; x<ArraySize(symbols); x++)
      if(symbols[x]!=Symbol()) // don't send SpyAgent to own chart
         if(iCustom(symbols[x],0,"SpyAgent",ChartID(),0)==INVALID_HANDLE)
           {
            Print("Error in setting of SpyAgent on "+symbols[x]);
            return;
           }
  }

Initメソッドは行のリスト(CListオブジェクトとして - CListはCObject 型の動的なリストです)を作成し、CTable の内部値のデフォルト値を設定します(フォント、フォントサイズ、色、セルの大きさとチャートの右上角からの距離)。

//+------------------------------------------------------------------+
//| Sets default parameters of the table                             |
//+------------------------------------------------------------------+
CTable::Init()
  {
//--- create list for storing row objects
   rowList=new CList;

//--- set defaults
   xDistance = 10;
   yDistance = 10;
   cellWidth = 60;
   cellHeight= 20;
   font="Arial";
   fontSize=10;
   fontColor=clrWhite;
  }

デストラクタは非常にシンプルです。行のリストとパネルによって作られた全てのチャートオブジェクト(ラベル)を削除します。

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CTable::~CTable()
  {
   int total=ObjectsTotal(0);

//--- remove all text labels from the chart (all object names starting with nameBase prefix)
   for(int i=total-1; i>=0; i--)
      if(StringFind(ObjectName(0,i),nameBase)!=-1)
         ObjectDelete(0,ObjectName(0,i));

//--- delete list of rows and free memory
   delete(rowList);
  }

AddRowメソッドは行のリストに新しい行を追加します。rowListはCListオブジェクトで、自動的にリサイズすることを覚えておいてください。このメソッドはまた、追加されたすべてのCRowオブジェクトのためにlnitメソッドを呼び出します。オブジェクトに自身の内部値を正しく初期化できるようにする必要があります。例えば、インディケーターやファイルハンドラを作成するためにlnit callを使うことができます。

//+------------------------------------------------------------------+
//| Appends new row to the end of the table                          |
//+------------------------------------------------------------------+
CTable::AddRow(CRow *row)
  {
   rowList.Add(row);
   row.Init(symbols,timeframes);
  }

Updateメソッドは少し複雑です。パネルの再描画に使用します。

基本的には、以下の3つのパートからの成ります:

  • 1列目の描画(行の名前)
  • 1つ目の行の描画(選択されたモードに応じたタイムフレームやシンボルの名前)
  • 内部セルの描画(コンポーネントの値)

与えられたシンボルやタイムフレームに基づいてすべてのコンポーネントに自身の値を計算するように求めることに注意してください。また、コンポーネントにはそれぞれフォントや色を決めさせます。

//+------------------------------------------------------------------+
//| Redraws the table                                                |
//+------------------------------------------------------------------+
CTable::Update()
  {
   CRow *row;
   string symbol;
   ENUM_TIMEFRAMES tf;

   int rows=rowList.Total(); // number of rows
   int columns;              // number of columns

   if(tfMode)
      columns=ArraySize(timeframes);
   else
      columns=ArraySize(symbols);

//--- draw first column (names of rows)
   for(int y=0; y<rows; y++)
     {
      row=(CRow*)rowList.GetNodeAtIndex(y);
      //--- note: we ask row object to return its name
      DrawLabel(columns,y+1,row.GetName(),font,fontColor);
     }

//--- draws first row (names of timeframes or currency pairs)
   for(int x=0; x<columns; x++)
     {
      if(tfMode)
         DrawLabel(columns-x-1,0,PeriodToString(timeframes[x]),font,fontColor);
      else
         DrawLabel(columns-x-1,0,symbols[x],font,fontColor);
     }

//--- draws inside table cells
   for(int y=0; y<rows; y++)
      for(int x=0; x<columns; x++)
        {
         row=(CRow*)rowList.GetNodeAtIndex(y);

         if(tfMode)
           {
            //--- in multi-timeframe mode use current symbol and different timeframes
            tf=timeframes[x];
            symbol=_Symbol;
           }
         else
           {
            //--- in multi-currency mode use current timeframe and different symbols
            tf=Period();
            symbol=symbols[x];
           }

         //--- note: we ask row object to return its font, 
         //--- color and current calculated value for given timeframe and symbol
         DrawLabel(columns-x-1,y+1,row.GetValue(symbol,tf),row.GetFont(symbol,tf),row.GetColor(symbol,tf));
        }

//--- forces chart to redraw
   ChartRedraw();
  }

DrawLabelメソッドはパネルの指定されたセルにあるテキストラベルを描画するために使われます。まずこのセルのためのラベルがすでに存在しているかをチェックします。もしも存在していなければ、新しく作成します。

そうすると、すべての必要なラベルプロパティとテキストが設定されます。

//+------------------------------------------------------------------+
//| Draws text label in the specified cell of the table              |
//+------------------------------------------------------------------+  
CTable::DrawLabel(int x,int y,string text,string font,color col)
  {
//--- create unique name for this cell
   string name=nameBase+IntegerToString(x)+":"+IntegerToString(y);

//--- create label
   if(ObjectFind(0,name)<0)
      ObjectCreate(0,name,OBJ_LABEL,0,0,0);

//--- set label properties
   ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_RIGHT_UPPER);
   ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_RIGHT_UPPER);
   ObjectSetInteger(0,name,OBJPROP_XDISTANCE,xDistance+x*cellWidth);
   ObjectSetInteger(0,name,OBJPROP_YDISTANCE,yDistance+y*cellHeight);
   ObjectSetString(0,name,OBJPROP_FONT,font);
   ObjectSetInteger(0,name,OBJPROP_COLOR,col);
   ObjectSetInteger(0,name,OBJPROP_FONTSIZE,fontSize);

//--- set label text
   ObjectSetString(0,name,OBJPROP_TEXT,text);
  }

他のメソッドはここでは紹介しません。非常に単純でそれほど重要ではないからです。全体のコードはこの記事の最後からダウンロードできます。


4. CRowの拡張

CRowはパネルによって使用できる、すべてのコンポーネントの親クラスです。

CRowのコードを見てみましょう:

//+------------------------------------------------------------------+
//| CRow class                                                       |
//+------------------------------------------------------------------+
//| Base class for creating custom table rows                        |
//| one or more methods of CRow should be overriden                  |
//| when creating own table rows                                     |
//+------------------------------------------------------------------+
class CRow : public CObject
  {
public:
   //--- default initialization method
   virtual void Init(string &symb[],ENUM_TIMEFRAMES &tfs[]) { }

   //--- default method for obtaining string value to display in the table cell
   virtual string GetValue(string symbol,ENUM_TIMEFRAMES tf) { return("-"); }

   //--- default method for obtaining color for table cell
   virtual color GetColor(string symbol,ENUM_TIMEFRAMES tf) { return(clrWhite); }
   
   //--- default method for obtaining row name
   virtual string GetName() { return("-"); }

   //--- default method for obtaining font for table cell
   virtual string GetFont(string symbol,ENUM_TIMEFRAMES tf) { return("Arial"); }
  };

それはCObjectを拡張します。CObjectだけがCListストラクチャに格納できるからです。このクラスには5つのメソッドがあり、ほとんどの空です。より正確にいうと、それらの多くはデフォルト値しか返しません。これらのメソッドはCRowを拡張した際にオーバーライドされるようにデザインされています。すべてではなく必要なものだけをオーバーライドすることができます。

できるだけ単純なパネルコンポーネントを作成してみましょう - 現在のプライスのコンポーネントです。マルチ通貨モードで使用され、様々な投資対象の価格を表示します。

そのために、以下のようなCpriceRowクラスを作成します:

//+------------------------------------------------------------------+
//| CPriceRow class                                                  |
//+------------------------------------------------------------------+
class CPriceRow : public CRow
  {
public:
   //--- overrides default GetValue(..) method from CRow
   virtual string    GetValue(string symbol,ENUM_TIMEFRAMES tf);

   //--- overrides default GetName() method from CRow
   virtual string    GetName();

  };
//+------------------------------------------------------------------+
//| Overrides default GetValue(..) method from CRow                  |
//+------------------------------------------------------------------+
string CPriceRow::GetValue(string symbol,ENUM_TIMEFRAMES tf)
  {
   MqlTick tick;

//--- gets current price
   if(!SymbolInfoTick(symbol,tick)) return("-");

   return(DoubleToString(tick.bid,(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS)));
  }
//+------------------------------------------------------------------+
//| Overrides default GetName() method from CRow                     |
//+------------------------------------------------------------------+
string CPriceRow::GetName()
  {
   return("Price");
  }

ここでオーバーライドするのはGetValueとGetNameメソッドです。GetNameはシンプルにこの行の名前を返し、パネルの1列目に表示します。GetValueはシンボルに対する最後のティックから最新のビッドプライスを返します。必要なのはこれだけです。

非常にシンプルでした。違うことをしてみましょう。現在のRSI値を示すコンポーネントを作成します。

コードは前のものと似ています:

//+------------------------------------------------------------------+
//| CRSIRow class                                                    |
//+------------------------------------------------------------------+
class CRSIRow : public CRow
  {
private:
   int               rsiPeriod;        // RSI period
   string            symbols[];        // symbols array
   ENUM_TIMEFRAMES   timeframes[];     // timeframes array
   int               handles[];        // array of RSI handles

   //--- finds the indicator handle for a given symbol and timeframe
   int               GetHandle(string symbol,ENUM_TIMEFRAMES tf);

public:
   //--- constructor
                     CRSIRow(int period);

   //--- overrides default GetValue(..) method from CRow
   virtual string    GetValue(string symbol,ENUM_TIMEFRAMES tf);

   //--- overrides default GetName() method from CRow
   virtual string    GetName();

   //--- overrides default Init(..) method from CRow
   virtual void      Init(string &symb[],ENUM_TIMEFRAMES &tfs[]);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CRSIRow::CRSIRow(int period)
  {
   rsiPeriod=period;
  }
//+------------------------------------------------------------------+
//| Overrides default Init(..) method from CRow                      |
//+------------------------------------------------------------------+
void CRSIRow::Init(string &symb[],ENUM_TIMEFRAMES &tfs[])
  {
   int size=ArraySize(symb);
   
   ArrayResize(symbols,size);
   ArrayResize(timeframes,size);
   ArrayResize(handles,size);
   
//--- copies arrays contents into own arrays
   ArrayCopy(symbols,symb);
   ArrayCopy(timeframes,tfs);
  
//--- gets RSI handles for all used symbols or timeframes
   for(int i=0; i<ArraySize(symbols); i++)
      handles[i]=iRSI(symbols[i],timeframes[i],rsiPeriod,PRICE_CLOSE);
  }
//+------------------------------------------------------------------+
//| Overrides default GetValue(..) method from CRow                  |
//+------------------------------------------------------------------+
string CRSIRow::GetValue(string symbol,ENUM_TIMEFRAMES tf)
  {
   double value[1];

//--- gets RSI indicator handle
   int handle=GetHandle(symbol,tf);

   if(handle==INVALID_HANDLE) return("err");

//--- gets current RSI value
   if(CopyBuffer(handle,0,0,1,value)<0) return("-");

   return(DoubleToString(value[0],2));
  }
//+------------------------------------------------------------------+
//| Overrides default GetName() method from CRow                     |
//+------------------------------------------------------------------+
string CRSIRow::GetName()
  {
   return("RSI("+IntegerToString(rsiPeriod)+")");
  }
//+------------------------------------------------------------------+
//| finds the indicator handle for a given symbol and timeframe      |
//+------------------------------------------------------------------+
int CRSIRow::GetHandle(string symbol,ENUM_TIMEFRAMES tf)
  {
   for(int i=0; i<ArraySize(timeframes); i++)
      if(symbols[i]==symbol && timeframes[i]==tf)
         return(handles[i]);

   return(INVALID_HANDLE);
  }

いくつかの新しいメソッドがあります。コンストラクタはRSIの期間をメンバ変数として格納します。Initメソッドを使いRSIインディケーターハンドルを作成します。このハンドルはhandles[] 配列に格納されます。GetValueメソッドは直近のRSIバッファの値をコピーし返します。Private GetHandleメソッドはhandles[] 配列から適切なインディケーターハンドラを見つけるのに使います。GetNameは名前の通りです。

このように、パネルのコンポーネントの作成はとても簡単です。同じ方法でカスタム条件のコンポーネントを作成することができます。インディケーターの値である必要はありません。以下にSMAに基づいたカスタム条件を示します。現在のプライスが移動平均より上かを確認し、”はい”か”いいえ”を表示します。

//+------------------------------------------------------------------+
//| CPriceMARow class                                                |
//+------------------------------------------------------------------+
class CPriceMARow : public CRow
  {
private:
   int               maPeriod; // period of moving average
   int               maShift;  // shift of moving average
   ENUM_MA_METHOD    maType;   // SMA, EMA, SMMA or LWMA
   string            symbols[];        // symbols array
   ENUM_TIMEFRAMES   timeframes[];     // timeframes array
   int               handles[];        // array of MA handles

   //--- finds the indicator handle for a given symbol and timeframe
   int               GetHandle(string symbol,ENUM_TIMEFRAMES tf);

public:
   //--- constructor
                     CPriceMARow(ENUM_MA_METHOD type,int period,int shift);

   //--- overrides default GetValue(..) method of CRow
   virtual string    GetValue(string symbol,ENUM_TIMEFRAMES tf);

   // overrides default GetName() method CRow
   virtual string    GetName();

   //--- overrides default Init(..) method from CRow
   virtual void      Init(string &symb[],ENUM_TIMEFRAMES &tfs[]);
  };
//+------------------------------------------------------------------+
//| CPriceMARow class constructor                                    |
//+------------------------------------------------------------------+
CPriceMARow::CPriceMARow(ENUM_MA_METHOD type,int period,int shift)
  {
   maPeriod= period;
   maShift = shift;
   maType=type;
  }
//+------------------------------------------------------------------+
//| Overrides default Init(..) method from CRow                      |
//+------------------------------------------------------------------+
void CPriceMARow::Init(string &symb[],ENUM_TIMEFRAMES &tfs[])
  {
   int size=ArraySize(symb);
   
   ArrayResize(symbols,size);
   ArrayResize(timeframes,size);
   ArrayResize(handles,size);
   
//--- copies arrays contents into own arrays
   ArrayCopy(symbols,symb);
   ArrayCopy(timeframes,tfs);
  
//--- gets MA handles for all used symbols or timeframes
   for(int i=0; i<ArraySize(symbols); i++)
      handles[i]=iMA(symbols[i],timeframes[i],maPeriod,maShift,maType,PRICE_CLOSE);
  }
//+------------------------------------------------------------------+
//| Overrides default GetValue(..) method of CRow                    |
//+------------------------------------------------------------------+
string CPriceMARow::GetValue(string symbol,ENUM_TIMEFRAMES tf)
  {
   double value[1];
   MqlTick tick;

//--- obtains MA indicator handle
   int handle=GetHandle(symbol,tf);

   if(handle==INVALID_HANDLE) return("err");

//--- gets the last MA value
   if(CopyBuffer(handle,0,0,1,value)<0) return("-");
//--- gets the last price
   if(!SymbolInfoTick(symbol,tick)) return("-");

//--- checking the condition: price > MA
   if(tick.bid>value[0])
      return("Yes");
   else
      return("No");
  }
//+------------------------------------------------------------------+
//| Overrides default GetName() method of CRow                       |
//+------------------------------------------------------------------+
string CPriceMARow::GetName()
  {
   string name;

   switch(maType)
     {
      case MODE_SMA: name = "SMA"; break;
      case MODE_EMA: name = "EMA"; break;
      case MODE_SMMA: name = "SMMA"; break;
      case MODE_LWMA: name = "LWMA"; break;
     }

   return("Price>"+name+"("+IntegerToString(maPeriod)+")");
  }
//+------------------------------------------------------------------+
//| finds the indicator handle for a given symbol and timeframe      |
//+------------------------------------------------------------------+
int CPriceMARow::GetHandle(string symbol,ENUM_TIMEFRAMES tf)
  {
   for(int i=0; i<ArraySize(timeframes); i++)
      if(symbols[i]==symbol && timeframes[i]==tf)
         return(handles[i]);

   return(INVALID_HANDLE);
  }

コードは長くなります 移動平均には3つのパラメータがあるからです:期間、シフト、タイプ。GetNameは少し複雑です、MAタイプと期間に基づいて名前を作成するからです。GetValueはCRSIRowとほとんど同じように動きますが、プライスをSMAと比較して、上なら”はい”、下なら”いいえ”を返します。

最後の例は少しだけ複雑です。CPriceChangeRowクラスです。現在の足のプライスの変化を示します。3つのモードで動きます:

  • 矢印の表示(上昇は緑、下落は赤);
  • プライスの変化の表示(上昇は緑、下落は赤);
  • プライスの変化率の表示(上昇は緑、下落は赤);

コードは以下のようになります:

//+------------------------------------------------------------------+
//| CPriceChangeRow class                                            |
//+------------------------------------------------------------------+
class CPriceChangeRow : public CRow
  {
private:
   bool              percentChange;
   bool              useArrows;

public:
   //--- constructor
                     CPriceChangeRow(bool arrows,bool percent=false);

   //--- overrides default GetName() method from CRow
   virtual string    GetName();

   //--- overrides default GetFont() method from CRow
   virtual string    GetFont(string symbol,ENUM_TIMEFRAMES tf);

   //--- overrides default GetValue(..) method from CRow
   virtual string    GetValue(string symbol,ENUM_TIMEFRAMES tf);

   //--- overrides default GetColor(..) method from CRow
   virtual color     GetColor(string symbol,ENUM_TIMEFRAMES tf);

  };
//+------------------------------------------------------------------+
//| CPriceChangeRow class constructor                                |
//+------------------------------------------------------------------+
CPriceChangeRow::CPriceChangeRow(bool arrows,bool percent=false)
  {
   percentChange=percent;
   useArrows=arrows;
  }
//+------------------------------------------------------------------+
//| Overrides default GetName() method from CRow                     |
//+------------------------------------------------------------------+
 string CPriceChangeRow::GetName()
  {
   return("PriceChg");
  }
//+------------------------------------------------------------------+
//| Overrides default GetFont() method from CRow                     |
//+------------------------------------------------------------------+
string CPriceChangeRow::GetFont(string symbol,ENUM_TIMEFRAMES tf)
  {
//--- we use Wingdings font to draw arrows (up/down)
   if(useArrows)
      return("Wingdings");
   else
      return("Arial");
  }
//+------------------------------------------------------------------+
//| Overrides default GetValue(..) method from CRow                  |
//+------------------------------------------------------------------+
string CPriceChangeRow::GetValue(string symbol,ENUM_TIMEFRAMES tf)
  {
   double close[1];
   double open[1];

//--- gets open and close of current bar
   if(CopyClose(symbol,tf,0, 1, close) < 0) return(" ");
   if(CopyOpen(symbol, tf, 0, 1, open) < 0) return(" ");

//--- current bar price change
   double change=close[0]-open[0];

   if(useArrows)
     {
      if(change > 0) return(CharToString(233)); // returns up arrow code
      if(change < 0) return(CharToString(234)); // returns down arrow code
      return(" ");
        }else{
      if(percentChange)
        {
         //--- calculates percent change
         return(DoubleToString(change/open[0]*100.0,3)+"%");
           }else{
         return(DoubleToString(change,(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS)));
        }
     }
  }
//+------------------------------------------------------------------+
//| Overrides default GetColor(..) method from CRow                  |
//+------------------------------------------------------------------+
color CPriceChangeRow::GetColor(string symbol,ENUM_TIMEFRAMES tf)
  {
   double close[1];
   double open[1];

//--- gets open and close of current bar
   if(CopyClose(symbol,tf,0, 1, close) < 0) return(clrWhite);
   if(CopyOpen(symbol, tf, 0, 1, open) < 0) return(clrWhite);

   if(close[0] > open[0]) return(clrLime);
   if(close[0] < open[0]) return(clrRed);
   return(clrWhite);
  }

コンストラクタには2つのパラメータがあります。1つ目は矢印を表示するかどうか決めるものです。真であれば、2つ目のパラメータは破棄されます。偽であれば、2つ目のパラメータにより、変化率かプライスの変化を示すかを決めます。

このクラスではCRowの4つのメソッドをオーバーライドすることにします: GetName、 GetValue、 GetColorとGetFontです。GetNameは最もシンプルで、名前を返すだけです。GetFontを使うことで、 Wingdingsフォントから矢印や記号などを表示できるようになります。GetColorはプライスは上昇ならライム色、下落なら赤を返します。プライスに変化がないか、エラーの場合は白を返します。GetValueは最後の足の始値と終値の差を得てその差を計算し、返します。アローモードでは、Wingdingsの記号コードから、上昇と下落の矢印のコードを返します。


5. すべての使い方

パネルを使うには、新しいインディケーターを作成しなければいけません。それをTableSampleと呼びましょう。

処理するハンドルは:

CTableオブジェクトを指すポインタが必要です。OnInit()内に動的に作成されます。まず最初に、どのモード(マルチタイムフレームかマルチ通貨)を使うかを決めなければいけません。以下のコードはマルチ通貨モードのものですが、マルチタイムフレームモードで必要なものもすべてコメントにあります。マルチ通貨モードではシンボルの配列を作り、それをCTableコンストラクタに渡さなければいけません。マルチタイムフレームモードではタイムフレームの配列を作り、それを第2のCTableコンストラクタに渡さなければいけません。

その後で、必要なコンポーネントを作成し、AddRowメソッドを使ってパネルに追加します。必須ではありませんが、パネルパラメータを調整することもできます。最後に、パネルの最初の描画をする必要があります。OnInit()の最後にUpdateを呼び出します。OnDeinitはシンプルです。それが行う唯一のことは、CTableオブジェクトの削除です。その結果、CTableデストラクタが呼び出されます。

OnCalculate(...)OnChartEvent(...)は同一です。Updateメソッドを呼び出すことしかできません。OnChartEvent(...)はパネルがマルチ通貨モードの場合のみ必要です。このモードでは、SpyAgentにより発生したイベントを処理します。マルチタイムフレームモードでは、現在のチャートのシンボルだけをモニターすれば良いのでOnCalculate(...)だけが必要です。

//+------------------------------------------------------------------+
//|                                                  TableSample.mq5 |
//|                                                 Marcin Konieczny |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Marcin Konieczny"
#property version   "1.00"
#property indicator_chart_window
#property indicator_plots 0

#include <Table.mqh>
#include <PriceRow.mqh>
#include <PriceChangeRow.mqh>
#include <RSIRow.mqh>
#include <PriceMARow.mqh>

CTable *table; // pointer to CTable object
//+------------------------------------------------------------------+
//| Indicator initialization function                                |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- timeframes used in table (in multi-timeframe mode)
   ENUM_TIMEFRAMES timeframes[4]={PERIOD_M1,PERIOD_H1,PERIOD_D1,PERIOD_W1};

//--- symbols used in table (in multi-currency mode)
   string symbols[4]={"EURUSD","GBPUSD","USDJPY","AUDCHF" };
//-- CTable object creation 
//   table = new CTable(timeframes); // multi-timeframe mode
   table=new CTable(symbols); // multi-currency mode

//--- adding rows to the table
   table.AddRow(new CPriceRow());               // shows current price
   table.AddRow(new CPriceChangeRow(false));     // shows change of price in the last bar
   table.AddRow(new CPriceChangeRow(false,true)); // shows percent change of price in the last bar
   table.AddRow(new CPriceChangeRow(true));      // shows change of price as arrows
   table.AddRow(new CRSIRow(14));                // shows RSI(14)
   table.AddRow(new CRSIRow(10));                // shows RSI(10)
   table.AddRow(new CPriceMARow(MODE_SMA,20,0));  // shows if SMA(20) > current price

//--- setting table parameters
   table.SetFont("Arial",10,clrYellow);  // font, size, color
   table.SetCellSize(60, 20);           // width, height
   table.SetDistance(10, 10);           // distance from upper right chart corner

   table.Update(); // forces table to redraw

   return(0);
  }
//+------------------------------------------------------------------+
//| Indicator deinitialization function                              |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- calls table destructor and frees memory
   delete(table);
  }
//+------------------------------------------------------------------+
//| Indicator iteration function                                     |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//--- update table: recalculate/repaint
   table.Update();
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| OnChartEvent handler                                             |
//| Handles CHARTEVENT_CUSTOM events sent by SpyAgent indicators     |
//| nedeed only in multi-currency mode!                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   table.Update(); // update table: recalculate/repaint
  }
//+------------------------------------------------------------------+

このインディケーターをチャートに張り付ければ、自動でアップデートが始まり、ついにパネルが使えるようになります。


6. インストレーション

すべてのファイルがコンパイルされている必要があります。SpyAgentとTableSampleがインディケーターでterminal_data_folder\MQL5\Indicatorsにコピーしておきます。その他のファイルはインクルードファイルなのでterminal_data_folder\MQL5\Include内に置いておきます。パネルを実行するには、TableSampleインディケーターをチャートのどれかに張り付けてください。SpyAgentを貼り付ける必要はありません。自動的に起動されます。


まとめ

この記事では、MetaTrader 5のマルチタイムフレームとマルチ通貨パネルのオブジェクト指向インプリメンテーションの仕方を解説しました。簡単に拡張でき、少ない労力でカスタマイズされたパネルを構築できるデザインを成し遂げる方法を示しました。

この記事のすべてのコードは以下からダウンロードできます。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/357

添付されたファイル |
セマフォインディケーターを使った簡単なトレーディングシステム セマフォインディケーターを使った簡単なトレーディングシステム
複雑なトレーディングシステムも、よく見てみると複数の簡単な取引シグナルに基づいていることがわかります。ですから、開発の初心者はすぐに複雑なアルゴリズムを書き始める必要はありません。この記事ではセマフォインディケーターを使って取引を行うトレーディングシステムの例を紹介します。
オブジェクト指向プログラミング オブジェクト指向プログラミング
オブジェクト指向プログラミングに関する多相性やカプセル化などについて理解する必要はありません。これらの機能を使うだけで良いのです。この記事ではオブジェクト指向プログラミングの基本を例を使って具体的に見ていきます。
6つのステップでトレーディング自動装置を作りましょう! 6つのステップでトレーディング自動装置を作りましょう!
もしどのようにトレード用クラス群がどのように組まれているかをご存知でなく、「オブジェクト指向プログラミング」という単語に恐怖を感じるようであれば、この記事はあなたにとって最適かもしれません。実際、売買シグナルのためのモジュールの作成のために詳しい内容を知る必要はありません。ただ、いくつかの簡単なルールに従ってください。作業は全てMQL5のウィザードに従って進められ、すぐに使用できる取引用自動システムを作ることができます!
カスタムグラフィックコントロール パート3. フォーム カスタムグラフィックコントロール パート3. フォーム
この記事はグラフィックコントロールに関する3つの記事の最後になります。代表的なグラフィカルインターフェースである、フォームの作成や、他のコントロールとの併用の仕方についても紹介します。コントロールライブラリーにはFormクラスの他に、CFrame、CButton、CLabelといったクラスが加えられました。