
マルチタイムフレームとマルチ通貨パネルの作成
はじめに
この記事では、オブジェクト指向プログラミングを使うMetaTrader 5のマルチタイムフレームとマルチ通貨パネルの作成の仕方を解説します。主な目的は、パネルのコードを変更することなく、価格や価格の変化、インディケーターの値や売買条件のカスタマイズなど多くの異なった種類のデータを表示することのできる汎用パネルの作成です。そうすることで、パネルを必要とするいろいろな方法でカスタマイズしたい時に必要となるコーディングをわずかにすることができます。
これから解説するソリューションは2つのモードで動きます:
- マルチタイムフレームモード - 現在のシンボルを異なるタイムフレームで表示するタイムテーブルで見ることができます。
- マルチ通貨モード - 異なるシンボルを現在のタイムフレームで表示するテーブルを見ることができます。
以下の図はそれぞれのモードでのパネルを表しています。
1つ目はマルチタイムフレームモードで動き、次のデータを表示します:
- 現在の価格;
- 現在の足の価格の変化;
- 現在の足の価格の変化率;
- 現在の足の価格の変化の矢印(上昇/下落);
- RSI(14)インディケーターの値;
- RSI(10)インディケーターの値;
- カスタムコンディション:SMA(20) >現在の価格。
図 1. マルチタイムフレームモード
2つ目はマルチ通貨モードで動き、次のデータを表示します:
- 現在の価格;
- 現在の足の価格の変化;
- 現在の足の価格の変化率;
- 現在の足の価格の変化の矢印(上昇/下落);
- RSI(10)インディケーターの値;
- RSI(14)インディケーターの値;
- カスタムコンディション:SMA(20) >現在の価格。
図 2. マルチ通貨モード
1. インプリメンテーション
以下のクラス図はパネルのインプリメンテーションデザインを表しています。
図 3. パネルのクラス図
図のそれぞれの要素を説明します:
- CTable. パネルのコアクラス。パネルの描画とそのコンポーネントの管理を担当します。
- SpyAgent.シンボル(投資対象)を”スパイする”インディケーターを管理します。それぞれのAgentは異なるシンボルに対し作成され送信されます。Agentは通貨ペアチャート上に新しいティックがある度にOnCalculateイベントに反応しCHARTEVENT_CUSTOM イベントをCTableオブジェクトに対し送信し、更新します。このアプローチに対するすべての考え方は記事、"The Implementation of a Multi-currency Mode in MetaTrader 5""(MetaTrader 5のマルチ通貨モードのインプリメンテーション)に基づいています。テクニカルな詳細はそこで見ることができます。
- CRow. パネルを作るためのすべてのインディケーターとコンディションの親クラス。このクラスを拡張することで、パネルに必要なすべてのコンポーネントを作成することが可能です。
- CPriceRow. CRowを拡張するシンプルなクラス、現在のビッドプライスを表示するのに使われます。
- CPriceChangeRow. CRowを拡張するクラス、現在の足のプライスの変化を表示するのに使われます。プライスの変化、変化率や矢印を表示することができます。
- CRSIRow. CRowを拡張するクラス、現在のRSIインディケーターの値を表示するのに使われます。
- 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と呼びましょう。
処理するハンドルは:
- OnInit();
- OnDeinit();
- OnCalculate(...);
- OnChartEvent(...) (マルチ通貨モードを使っている場合のみ)
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





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索