MQL5 クックブック:単一ウィンドウでのマルチ タイム フレームのモニター
はじめに
ポジションオープンの方向を選択するとき、同時に表示されるマルチタイムフレームを持つ価格チャートがひじょうに便利だと思われます。MetaTrader 5 クライアントターミナルには解析用タイムフレームが21備わっています。そういったウィンドウでは特別なチャートオブジェクトを利用することができます。既存のチャートにそのチャートオブジェクトを入れ、シンボル、タイムフレーム、その他プロパティを設定することができるのです。そのようなチャートオブジェクトはいくつでも追加可能ですが、それをマニュアルでしようとするとかなり不便で時間のかかるものです。その上、チャートプロパティがすべてマニュアルモードで設定できるとは限りません。
本稿ではそのようなグラフィカルオブジェクトを詳しく考察していきます。説明のために、同時にサブウィンドウにマルチチャートオブジェクトを設定することができるコントロール(ボタン)を持つインディケータを作成します。またチャートオブジェクトは正確にサブウィンドウにフィットし、チャートまたはターミナルのサイズが変更されると自動で調整されます。
チャートオブジェクトを追加するためのボタンに加え、チャートプロパティを有効化/無効化するためのボタンも持ちます。それにはプログラムで変更を加えることのできるものも含みます。
作成
次のようにマニュアルでチャートオブジェクトを追加することもできます。挿入メニュー→オブジェクト→グラフィカルオブジェクト→チャート。たとえばこれはタイムフレームH4 と D1のオブジェクトの1時間チャートへの表示です。
図1 チャートオブジェクト
オブジェクトのパラメータを変更すると限られたプロパティのみ処理することができます。
図2 チャートオブジェクトのプロパティ
それでも売りや買い価格レベル、チャートの右端からのインデント、トレードレベルなどといったようなパラメータは適切にプログラムされた場合にのみ表示されます。
ではインディケータの作成を始めます。たとえばそれにChartObjects (項目のワーキングタイトル)と名前を付けます。MQL5 ウィザードを使って MetaEditor でインディケータ用テンプレートを作成します。カスタムインディケータプログラムのイベントハンドラを選択するとき、下のスクリーンショットとして表示されるものを選びます。
図3 インディケータのイベントハンドラ
結果、MetaEditorで開かれる場合、テンプレートのソースコードは以下のようなものとなります。
//+------------------------------------------------------------------+ //| ChartObjects.mq5 | //| Copyright 2013, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2013, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.00" #property indicator_chart_window //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { //--- } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- } //+------------------------------------------------------------------+
今回の実装には基本的に OnCalculate() 関数は必要ありませんが、これを使わずにインディケータをコンパイルすることはできません。また、主要な関数のひとつである OnDeinit() が必要となります。この関数はチャートからのプログラム削除を監視します。テンプレートの一次処理の次には、以下のソースコードを書きます。
//+------------------------------------------------------------------+ //| ChartObjects.mq5 | //| Copyright 2013, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2013, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.00" //--- #property indicator_chart_window // Indicator is in the main window #property indicator_plots 0 // Zero plotting series //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set the short name for the indicator IndicatorSetString(INDICATOR_SHORTNAME,"TimeFramesPanel"); //--- Initialization completed successfully return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Indicator deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- If the indicator has been deleted from the chart if(reason==REASON_REMOVE) { } } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- } //+------------------------------------------------------------------+
ここでチャートオブジェクトの格納場所(サブウィンドウ)として使われるインディケータを作成する必要があります。それは基本的にダミーのインディケータです。それに SubWindow と名前をつけます。以下がそのコードです。
//+------------------------------------------------------------------+ //| SubWindow.mq5 | //| Copyright 2013, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2013, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.00" //--- #property indicator_chart_window // Indicator is in the subwindow #property indicator_plots 0 // Zero plotting series //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set the short name for the indicator IndicatorSetString(INDICATOR_SHORTNAME,"SubWindow"); //--- Initialization completed successfully return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
SubWindow.ex5 インディケータはコンパイル後、 ChartObjects.ex5 内にリソースとして格納されます。よってプログラム開発者は最終的にエンドユーザーに対して 2 件ではなくファイルを 1 件だけ提供することとなります。
『MQL5 クックブック:MetaTrader 5 のトレードイベントへの音声通知』という先行記事ですでにお話したとおり、リソースファイルは #resource 命令を使用するプログラムにインクルードすることができます。そこでわれわれのプログラム ChartObjects の冒頭では以下のコード文字列を追加する必要があります。
//--- Include indicator resource #resource "\\Indicators\\SubWindow.ex5"
それから #define 命令を用いてコントロールに帰属する配列サイズを設定します。
//--- Number of time frame buttons #define TIMEFRAME_BUTTONS 21 //--- Number of buttons for chart object properties #define PROPERTY_BUTTONS 5
また通常通りプログラムの冒頭でグローバル変数を宣言します。
//--- Location of the SubWindow indicator in the resource string subwindow_path ="::Indicators\\SubWindow.ex5"; int subwindow_number =-1; // Subwindow number int subwindow_handle =INVALID_HANDLE; // SubWindow indicator handle string subwindow_shortname ="SubWindow"; // Short name of the indicator //--- int chart_width =0; // Chart width int chart_height =0; // Chart height int chart_scale =0; // Chart scale //--- color cOffButtonFont =clrWhite; // Unclicked button text color color cOffButtonBackground =clrDarkSlateGray; // Unclicked button background color color cOffButtonBorder =clrLightGray; // Unclicked button border color //--- color cOnButtonFont =clrGold; // Clicked button text color color cOnButtonBackground =C'28,47,47'; // Clicked button background color color cOnButtonBorder =clrLightGray; // Clicked button border color
この次はタイムフレームボタン用配列の宣言をします。
//--- Array of object names for time frame buttons string timeframe_button_names[TIMEFRAME_BUTTONS]= { "button_M1","button_M2","button_M3","button_M4","button_M5","button_M6","button_M10", "button_M12","button_M15","button_M20","button_M30","button_H1","button_H2","button_H3", "button_H4","button_H6","button_H8","button_H12","button_D1","button_W1","button_MN" }; //--- Array of text displayed on time frame buttons string timeframe_button_texts[TIMEFRAME_BUTTONS]= { "M1","M2","M3","M4","M5","M6","M10", "M12","M15","M20","M30","H1","H2","H3", "H4","H6","H8","H12","D1","W1","MN" }; //--- Array of time frame button states bool timeframe_button_states[TIMEFRAME_BUTTONS]={false};
チャートオブジェクトプロパティを制御するボタンの配列です。
//--- Array of object names for buttons of chart properties string property_button_names[PROPERTY_BUTTONS]= { "property_button_date","property_button_price", "property_button_ohlc","property_button_askbid", "property_button_trade_levels" }; //--- Array of text displayed on buttons of chart properties string property_button_texts[PROPERTY_BUTTONS]= { "Date","Price","OHLC","Ask / Bid","Trade Levels" }; //--- Array of states for buttons of chart properties bool property_button_states[PROPERTY_BUTTONS]={false}; //--- Array of sizes for buttons of chart properties int property_button_widths[PROPERTY_BUTTONS]= { 66,68,66,100,101 };
最後にチャートオブジェクト名の配列です。
//--- Array of chart object names string chart_object_names[TIMEFRAME_BUTTONS]= { "chart_object_m1","chart_object_m2","chart_object_m3","chart_object_m4","chart_object_m5","chart_object_m6","chart_object_m10", "chart_object_m12","chart_object_m15","chart_object_m20","chart_object_m30","chart_object_h1","chart_object_h2","chart_object_h3", "chart_object_h4","chart_object_h6","chart_object_h8","chart_object_h12","chart_object_d1","chart_object_w1","chart_object_mn" };
グラフィカルオブジェクト間の連携に関連する関数に進む前に、まずチャートにこういったオブジェクトを作成する関数を書きます。われわれのプログラムでは 2 タイプのグラフィカルオブジェクトが必要となります。それは OBJ_BUTTON および OBJ_CHART です。
ボタンは CreateButton() 関数で作成されます。
//+------------------------------------------------------------------+ //| Creating the Button object | //+------------------------------------------------------------------+ void CreateButton(long chart_id, // chart id int window_number, // window number string name, // object name string text, // displayed name ENUM_ANCHOR_POINT anchor, // anchor point ENUM_BASE_CORNER corner, // chart corner string font_name, // font int font_size, // font size color font_color, // font color color background_color, // background color color border_color, // border color int x_size, // width int y_size, // height int x_distance, // X-coordinate int y_distance, // Y-coordinate long z_order) // Z-order { //--- If the object has been created successfully if(ObjectCreate(chart_id,name,OBJ_BUTTON,window_number,0,0)) { // set its properties ObjectSetString(chart_id,name,OBJPROP_TEXT,text); // setting name ObjectSetString(chart_id,name,OBJPROP_FONT,font_name); // setting font ObjectSetInteger(chart_id,name,OBJPROP_COLOR,font_color); // setting font color ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,background_color); // setting background color ObjectSetInteger(chart_id,name,OBJPROP_BORDER_COLOR,border_color); // setting border color ObjectSetInteger(chart_id,name,OBJPROP_ANCHOR,anchor); // setting anchor point ObjectSetInteger(chart_id,name,OBJPROP_CORNER,corner); // setting chart corner ObjectSetInteger(chart_id,name,OBJPROP_FONTSIZE,font_size); // setting font size ObjectSetInteger(chart_id,name,OBJPROP_XSIZE,x_size); // setting width ObjectSetInteger(chart_id,name,OBJPROP_YSIZE,y_size); // setting height ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x_distance); // setting X-coordinate ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y_distance); // setting Y-coordinate ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false); // object is not available for selection ObjectSetInteger(chart_id,name,OBJPROP_STATE,false); // button state (clicked/unclicked) ObjectSetInteger(chart_id,name,OBJPROP_ZORDER,z_order); // Z-order for getting the click event ObjectSetString(chart_id,name,OBJPROP_TOOLTIP,"\n"); // no tooltip } }
それに応じて、サブウィンドウ内でのチャート作成は CreateChartInSubwindow() 関数によって行われます。
//+------------------------------------------------------------------+ //| Creating a chart object in a subwindow | //+------------------------------------------------------------------+ void CreateChartInSubwindow(int window_number, // subwindow number int x_distance, // X-coordinate int y_distance, // Y-coordinate int x_size, // width int y_size, // height string name, // object name string symbol, // symbol ENUM_TIMEFRAMES timeframe, // time frame int subchart_scale, // bar scale bool show_dates, // show date scale bool show_prices, // show price scale bool show_ohlc, // show OHLC prices bool show_ask_bid, // show ask/bid levels bool show_levels, // show trade levels string tooltip) // tooltip { //--- If the object has been created successfully if(ObjectCreate(0,name,OBJ_CHART,window_number,0,0)) { //--- Set the properties of the chart object ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_UPPER); // chart corner ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x_distance); // X-coordinate ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y_distance); // Y-coordinate ObjectSetInteger(0,name,OBJPROP_XSIZE,x_size); // width ObjectSetInteger(0,name,OBJPROP_YSIZE,y_size); // height ObjectSetInteger(0,name,OBJPROP_CHART_SCALE,subchart_scale); // bar scale ObjectSetInteger(0,name,OBJPROP_DATE_SCALE,show_dates); // date scale ObjectSetInteger(0,name,OBJPROP_PRICE_SCALE,show_prices); // price scale ObjectSetString(0,name,OBJPROP_SYMBOL,symbol); // symbol ObjectSetInteger(0,name,OBJPROP_PERIOD,timeframe); // time frame ObjectSetString(0,name,OBJPROP_TOOLTIP,tooltip); // tooltip ObjectSetInteger(0,name,OBJPROP_BACK,false); // object in the foreground ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); // object is not available for selection ObjectSetInteger(0,name,OBJPROP_COLOR,clrWhite); // white color //--- Get the chart object identifier long subchart_id=ObjectGetInteger(0,name,OBJPROP_CHART_ID); //--- Set the special properties of the chart object ChartSetInteger(subchart_id,CHART_SHOW_OHLC,show_ohlc); // OHLC ChartSetInteger(subchart_id,CHART_SHOW_TRADE_LEVELS,show_levels); // trade levels ChartSetInteger(subchart_id,CHART_SHOW_BID_LINE,show_ask_bid); // bid level ChartSetInteger(subchart_id,CHART_SHOW_ASK_LINE,show_ask_bid); // ask level ChartSetInteger(subchart_id,CHART_COLOR_LAST,clrLimeGreen); // color of the level of the last executed deal ChartSetInteger(subchart_id,CHART_COLOR_STOP_LEVEL,clrRed); // color of Stop order levels //--- Refresh the chart object ChartRedraw(subchart_id); } }
上記コードではまずチャートオブジェクト用に標準的なチャートプロパティを設定します。チャートオブジェクトの識別子を取得したあと、特殊なプロパティを設定します。ChartRedraw() 関数を用いてチャートオブジェクトをリフレッシュすることも重要です。それにはチャートオブジェクト識別子が渡されます。
2つの関数間でのコントロールの設定を分割します。AddTimeframeButtons() および AddPropertyButtons()です。
//+------------------------------------------------------------------+ //| Adding time frame buttons | //+------------------------------------------------------------------+ void AddTimeframeButtons() { int x_dist =1; // Indent from the left side of the chart int y_dist =125; // Indent from the bottom of the chart int x_size =28; // Button width int y_size =20; // Button height //--- for(int i=0; i<TIMEFRAME_BUTTONS; i++) { //--- If 7 buttons have already been added to the same row, set the coordinates for the next row if(i%7==0) { x_dist=1; y_dist-=21; } //--- Add a time frame button CreateButton(0,0,timeframe_button_names[i],timeframe_button_texts[i], ANCHOR_LEFT_LOWER,CORNER_LEFT_LOWER,"Arial",8, cOffButtonFont,cOffButtonBackground,cOffButtonBorder, x_size,y_size,x_dist,y_dist,3); //--- Set the X-coordinate for the next button x_dist+=x_size+1; } } //+------------------------------------------------------------------+ //| Adding buttons of chart properties | //+------------------------------------------------------------------+ void AddPropertyButtons() { int x_dist =1; // Indent from the left side of the chart int y_dist =41; // Indent from the bottom of the chart int x_size =66; // Button width int y_size =20; // Button height //--- for(int i=0; i<PROPERTY_BUTTONS; i++) { //--- If the first three buttons have already been added, set the coordinates for the next row if(i==3) { x_dist=1; y_dist-=21; } //--- Add a button CreateButton(0,0,property_button_names[i],property_button_texts[i], ANCHOR_LEFT_LOWER,CORNER_LEFT_LOWER,"Arial",8, cOffButtonFont,cOffButtonBackground,cOffButtonBorder, property_button_widths[i],y_size,x_dist,y_dist,3); //--- Set the X-coordinate for the next button x_dist+=property_button_widths[i]+1; } }
チャートからインディケータを削除する際、プログラムによって作成されるオブジェクトも削除する必要があります。このためには以下の補助関数が必要となります。
//+------------------------------------------------------------------+ //| Deleting the panel with time frame buttons | //+------------------------------------------------------------------+ void DeleteTimeframeButtons() { for(int i=0; i<TIMEFRAME_BUTTONS; i++) DeleteObjectByName(timeframe_button_names[i]); } //+------------------------------------------------------------------+ //| Deleting the panel with buttons of chart properties | //+------------------------------------------------------------------+ void DeletePropertyButtons() { for(int i=0; i<PROPERTY_BUTTONS; i++) DeleteObjectByName(property_button_names[i]); } //+------------------------------------------------------------------+ //| Deleting objects by name | //+------------------------------------------------------------------+ void DeleteObjectByName(string object_name) { //--- If such object exists if(ObjectFind(ChartID(),object_name)>=0) { //--- Delete it or print the relevant error message if(!ObjectDelete(ChartID(),object_name)) Print("Error ("+IntegerToString(GetLastError())+") when deleting the object!"); } }
インディケータをロードするときパネルがチャート上に設定されていること、チャートからインディケータを削除するときパネルオブジェクトがすべて削除されていることを確認するためハンドラ関数 OnInit() および OnDeinit()に以下の文字列を追加する必要があります。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Add the panel with time frame buttons to the chart AddTimeframeButtons(); //--- Add the panel with buttons of chart properties to the chart AddPropertyButtons(); //--- Redraw the chart ChartRedraw(); //--- Initialization completed successfully return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Indicator deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- If the indicator has been deleted from the chart if(reason==REASON_REMOVE) { //--- Delete buttons DeleteTimeframeButtons(); DeletePropertyButtons(); //--- Redraw the chart ChartRedraw(); } }
ここでインディケータがコンパイルしチャートにアタッチするならば、そのパネルは以下のスクリーンショットのように表示されるはずです。
図4 ボタン付きパネル
これでユーザー-パネル間の連携用の関数の作成をスタートする準備ができました。実質上それらはすべてメインの関数 OnChartEvent() から呼ばれることとなります。本稿ではこの関数で処理されるイベントを2種類考察します。
- CHARTEVENT_OBJECT_CLICK -グラフィカルオブジェクトのクリックイベント
- CHARTEVENT_CHART_CHANGE -プロパティダイアログウィンドウを用いてチャートのサイズを変更したりチャートプロパティを変更するイベント
ではイベント CHARTEVENT_OBJECT_CLICK から始めましょう。これから書こうとしている関数 ChartEventObjectClick() はすべての引数を OnChartEvent() 関数(その他別のイベントに対しては似たような関数を作成します)から取得します。
//+------------------------------------------------------------------+ //| Event of the click on a graphical object | //+------------------------------------------------------------------+ bool ChartEventObjectClick(int id, long lparam, double dparam, string sparam) { //--- Click on a graphical object if(id==CHARTEVENT_OBJECT_CLICK) { //--- If a time frame button has been clicked, set/delete 'SubWindow' and a chart object if(ToggleSubwindowAndChartObject(sparam)) return(true); //--- If a button of chart properties has been clicked, set/delete the property in chart objects if(ToggleChartObjectProperty(sparam)) return(true); } //--- return(false); }
ChartEventObjectClick() 関数のコードはシンプルです。パネル上のボタンのクリックイベントは識別子によって決められます。それから実装ロジックは2方向に分けられます。それはタイムフレームボタン上でクリックイベントを処理、またはチャートプロパティのボタン上でクリックイベントを処理、の2つです。左クリックされたオブジェクト名を持つ sparam 文字列パラメータが対応する関数 ToggleSubwindowAndChartObject() および ToggleChartObjectProperty() に渡されます。
これら関数のソースコードを考察します。関数 ToggleSubwindowAndChartObject() から始めます。
//+------------------------------------------------------------------+ //| Setting/deleting SubWindow and a chart object | //+------------------------------------------------------------------+ bool ToggleSubwindowAndChartObject(string clicked_object_name) { //--- Make sure that the click was on the time frame button object if(CheckClickOnTimeframeButton(clicked_object_name)) { //--- Check if the SubWindow exists subwindow_number=ChartWindowFind(0,subwindow_shortname); //--- If the SubWindow does not exist, set it if(subwindow_number<0) { //--- If the SubWindow is set if(AddSubwindow()) { //--- Add chart objects to it AddChartObjectsToSubwindow(clicked_object_name); return(true); } } //--- If the SubWindow exists if(subwindow_number>0) { //--- Add chart objects to it AddChartObjectsToSubwindow(clicked_object_name); return(true); } } //--- return(false); }
上記コードに施されたコメントによって実装ロジックは簡単に理解できるはずです。強調表示されている文字列はそのコードがこのずっと下にあるカスタム関数を持ちます。
CheckClickOnTimeframeButton() 関数はクリックされるボタンがタイムフレームのパネルに関連付けられている場合真を返します。
//+------------------------------------------------------------------+ //| Checking if a time frame button has been clicked | //+------------------------------------------------------------------+ bool CheckClickOnTimeframeButton(string clicked_object_name) { //--- Iterate over all time frame buttons and check the names for(int i=0; i<TIMEFRAME_BUTTONS; i++) { //--- Report the match if(clicked_object_name==timeframe_button_names[i]) return(true); } //--- return(false); }
タイムフレームボタンでのクリックが確認されると、 SubWindow がメインチャートに追加されているかどうか確認します。追加されていなければ、AddSubwindow() 関数を用いてそれを設定します。
//+------------------------------------------------------------------+ //| Adding a subwindow for chart objects | //+------------------------------------------------------------------+ bool AddSubwindow() { //--- Get the "SubWindow" indicator handle subwindow_handle=iCustom(_Symbol,_Period,subwindow_path); //--- If the handle has been obtained if(subwindow_handle!=INVALID_HANDLE) { //--- Determine the number of windows in the chart for the subwindow number subwindow_number=(int)ChartGetInteger(0,CHART_WINDOWS_TOTAL); //--- Add the SubWindow to the chart if(!ChartIndicatorAdd(0,subwindow_number,subwindow_handle)) Print("Failed to add the SUBWINDOW indicator ! "); //--- The subwindow exists else return(true); } //--- There is no subwindow return(false); }
それから AddChartObjectsToSubwindow() 関数を用いて作成したサブウィンドウにチャートオブジェクトを追加します。
//+------------------------------------------------------------------+ //| Adding chart objects to the subwindow | //+------------------------------------------------------------------+ void AddChartObjectsToSubwindow(string clicked_object_name) { ENUM_TIMEFRAMES tf =WRONG_VALUE; // Time frame string object_name =""; // Object name string object_text =""; // Object text int x_distance =0; // X-coordinate int total_charts =0; // Total chart objects int chart_object_width =0; // Chart object width //--- Get the bar scale and SubWindow height/width chart_scale=(int)ChartGetInteger(0,CHART_SCALE); chart_width=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS,subwindow_number); chart_height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,subwindow_number); //--- Get the number of chart objects in the SUBWINDOW total_charts=ObjectsTotal(0,subwindow_number,OBJ_CHART); //--- If there are no chart objects if(total_charts==0) { //--- Check if a time frame button has been clicked if(CheckClickOnTimeframeButton(clicked_object_name)) { //--- Initialize the array of time frame buttons InitializeTimeframeButtonStates(); //--- Get the time frame button text for the chart object tooltip object_text=ObjectGetString(0,clicked_object_name,OBJPROP_TEXT); //--- Get the time frame for the chart object tf=StringToTimeframe(object_text); //--- Set the chart object CreateChartInSubwindow(subwindow_number,0,0,chart_width,chart_height, "chart_object_"+object_text,_Symbol,tf,chart_scale, property_button_states[0],property_button_states[1], property_button_states[2],property_button_states[3], property_button_states[4],object_text); //--- Refresh the chart and exit ChartRedraw(); return; } } //--- If chart objects already exist in the SubWindow if(total_charts>0) { //--- Get the number of clicked time frame buttons and initialize the array of states int pressed_buttons_count=InitializeTimeframeButtonStates(); //--- If there are no clicked buttons, delete the SubWindow if(pressed_buttons_count==0) DeleteSubwindow(); //--- If the clicked buttons exist else { //--- Delete all chart objects from the subwindow ObjectsDeleteAll(0,subwindow_number,OBJ_CHART); //--- Get the width for chart objects chart_object_width=chart_width/pressed_buttons_count; //--- Iterate over all buttons in a loop for(int i=0; i<TIMEFRAME_BUTTONS; i++) { //--- If the button is clicked if(timeframe_button_states[i]) { //--- Get the time frame button text for the chart object tooltip object_text=ObjectGetString(0,timeframe_button_names[i],OBJPROP_TEXT); //--- Get the time frame for the chart object tf=StringToTimeframe(object_text); //--- Set the chart object CreateChartInSubwindow(subwindow_number,x_distance,0,chart_object_width,chart_height, chart_object_names[i],_Symbol,tf,chart_scale, property_button_states[0],property_button_states[1], property_button_states[2],property_button_states[3], property_button_states[4],object_text); //--- Determine the X-coordinate for the next chart object x_distance+=chart_object_width; } } } } //--- Refresh the chart ChartRedraw(); }
上記コードにつけられている詳細コメントが関数処理に対する理解をする上で役に立つことでしょう。これまで出てきたことのないカスタム関数は強調表示しています。
InitializeTimeframeButtonStates() 関数はクリックされたタイムフレームボタン数を返し、対応している状態の配列を初期化します。それはボタン状態によって色設定も行います。
//+------------------------------------------------------------------+ //| Initializing array of time frame button states and | //| returning the number of clicked buttons | //+------------------------------------------------------------------+ int InitializeTimeframeButtonStates() { //--- Counter of the clicked time frame buttons int pressed_buttons_count=0; //--- Iterate over all time frame buttons and count the clicked ones for(int i=0; i<TIMEFRAME_BUTTONS; i++) { //--- If the button is clicked if(ObjectGetInteger(0,timeframe_button_names[i],OBJPROP_STATE)) { //--- Indicate it in the current index of the array timeframe_button_states[i]=true; //--- Set clicked button colors ObjectSetInteger(0,timeframe_button_names[i],OBJPROP_COLOR,cOnButtonFont); ObjectSetInteger(0,timeframe_button_names[i],OBJPROP_BGCOLOR,cOnButtonBackground); //--- Increase the counter by one pressed_buttons_count++; } else { //--- Set unclicked button colors ObjectSetInteger(0,timeframe_button_names[i],OBJPROP_COLOR,cOffButtonFont); ObjectSetInteger(0,timeframe_button_names[i],OBJPROP_BGCOLOR,cOffButtonBackground); //--- Indicate that the button is unclicked timeframe_button_states[i]=false; } } //--- Return the number of clicked buttons return(pressed_buttons_count); }
DeleteSubwindow() 関数はひじょうにシンプルです。チャート用サブウィンドウの有無をチェックし、それを削除します。
//+------------------------------------------------------------------+ //| Deleting subwindow for chart objects | //+------------------------------------------------------------------+ void DeleteSubwindow() { //--- If the SubWindow exists if((subwindow_number=ChartWindowFind(0,subwindow_shortname))>0) { //--- Delete it if(!ChartIndicatorDelete(0,subwindow_number,subwindow_shortname)) Print("Failed to delete the "+subwindow_shortname+" indicator!"); } }
ここでチャートオブジェクトのプロパティを詳しく見ます。別の言い方をすれば、ChartEventObjectClick() 関数に戻り ToggleChartObjectProperty() 関数を考察します。クリックされるオブジェクト名もそれに渡されます。
//+------------------------------------------------------------------+ //| Setting/deleting chart object property | //| depending on the clicked button state | //+------------------------------------------------------------------+ bool ToggleChartObjectProperty(string clicked_object_name) { //--- If the "Date" button is clicked if(clicked_object_name=="property_button_date") { //--- If the button is clicked if(SetButtonColor(clicked_object_name)) ShowDate(true); //--- If the button is unclicked else ShowDate(false); //--- Refresh the chart and exit ChartRedraw(); return(true); } //--- If the "Price" button is clicked if(clicked_object_name=="property_button_price") { //--- If the button is clicked if(SetButtonColor(clicked_object_name)) ShowPrice(true); //--- If the button is unclicked else ShowPrice(false); //--- Refresh the chart and exit ChartRedraw(); return(true); } //--- If the "OHLC" button is clicked if(clicked_object_name=="property_button_ohlc") { //--- If the button is clicked if(SetButtonColor(clicked_object_name)) ShowOHLC(true); //--- If the button is unclicked else ShowOHLC(false); //--- Refresh the chart and exit ChartRedraw(); return(true); } //--- If the "Ask/Bid" button is clicked if(clicked_object_name=="property_button_askbid") { //--- If the button is clicked if(SetButtonColor(clicked_object_name)) ShowAskBid(true); //--- If the button is unclicked else ShowAskBid(false); //--- Refresh the chart and exit ChartRedraw(); return(true); } //--- If the "Trade Levels" button is clicked if(clicked_object_name=="property_button_trade_levels") { //--- If the button is clicked if(SetButtonColor(clicked_object_name)) ShowTradeLevels(true); //--- If the button is unclicked else ShowTradeLevels(false); //--- Refresh the chart and exit ChartRedraw(); return(true); } //--- No matches return(false); }
上記コードではクリックされたオブジェクトはチャートプロパティに関連するオブジェクト名と連続して比較されます。一致すれば SetButtonColor() 関数内でボタンがクリックされたかされていないか確認し、関連するボタンの色を設定します。
//+------------------------------------------------------------------+ //| Setting color of button elements depending on the state | //+------------------------------------------------------------------+ bool SetButtonColor(string clicked_object_name) { //--- If the button is clicked if(ObjectGetInteger(0,clicked_object_name,OBJPROP_STATE)) { //--- Set clicked button colors ObjectSetInteger(0,clicked_object_name,OBJPROP_COLOR,cOnButtonFont); ObjectSetInteger(0,clicked_object_name,OBJPROP_BGCOLOR,cOnButtonBackground); return(true); } //--- If the button is unclicked if(!ObjectGetInteger(0,clicked_object_name,OBJPROP_STATE)) { //--- Set unclicked button colors ObjectSetInteger(0,clicked_object_name,OBJPROP_COLOR,cOffButtonFont); ObjectSetInteger(0,clicked_object_name,OBJPROP_BGCOLOR,cOffButtonBackground); return(false); } //--- return(false); }
SetButtonColor() 関数はボタン状態を返します。この属性に応じてプログラムは関連する関数に SubWindowのチャートオブジェクトすべてで特定プロパティが有効または無効となることを通知します。各プロパティに対して書かれる個別の関数があります。以下は対応する関数コードコードです。
//+------------------------------------------------------------------+ //| Enabling/disabling dates for all chart objects | //+------------------------------------------------------------------+ void ShowDate(bool state) { int total_charts =0; // Number of objects string chart_name =""; // Chart object name //--- Check if the SubWindow exists // If it exists, then if((subwindow_number=ChartWindowFind(0,subwindow_shortname))>0) { //--- Get the number of chart objects total_charts=ObjectsTotal(0,subwindow_number,OBJ_CHART); //--- Iterate over all chart objects in a loop for(int i=0; i<total_charts; i++) { //--- Get the chart object name chart_name=ObjectName(0,i,subwindow_number,OBJ_CHART); //--- Set the property ObjectSetInteger(0,chart_name,OBJPROP_DATE_SCALE,state); } //--- Set the button state to the relevant index if(state) property_button_states[0]=true; else property_button_states[0]=false; //--- Refresh the chart ChartRedraw(); } } //+------------------------------------------------------------------+ //| Enabling/disabling prices for all chart objects | //+------------------------------------------------------------------+ void ShowPrice(bool state) { int total_charts =0; // Number of objects string chart_name =""; // Chart object name //--- Check if the SubWindow exists // If it exists, then if((subwindow_number=ChartWindowFind(0,subwindow_shortname))>0) { //--- Get the number of chart objects total_charts=ObjectsTotal(0,subwindow_number,OBJ_CHART); //--- Iterate over all chart objects in a loop for(int i=0; i<total_charts; i++) { //--- Get the chart object name chart_name=ObjectName(0,i,subwindow_number,OBJ_CHART); //--- Set the property ObjectSetInteger(0,chart_name,OBJPROP_PRICE_SCALE,state); } //--- Set the button state to the relevant index if(state) property_button_states[1]=true; else property_button_states[1]=false; //--- Refresh the chart ChartRedraw(); } } //+------------------------------------------------------------------+ //| Enabling/disabling OHLC for all chart objects | //+------------------------------------------------------------------+ void ShowOHLC(bool state) { int total_charts =0; // Number of objects long subchart_id =0; // Chart object identifier string chart_name =""; // Chart object name //--- Check if the SubWindow exists // If it exists, then if((subwindow_number=ChartWindowFind(0,subwindow_shortname))>0) { //--- Get the number of chart objects total_charts=ObjectsTotal(0,subwindow_number,OBJ_CHART); //--- Iterate over all chart objects in a loop for(int i=0; i<total_charts; i++) { //--- Get the chart object name chart_name=ObjectName(0,i,subwindow_number,OBJ_CHART); //--- Get the chart object identifier subchart_id=ObjectGetInteger(0,chart_name,OBJPROP_CHART_ID); //--- Set the property ChartSetInteger(subchart_id,CHART_SHOW_OHLC,state); //--- Refresh the chart object ChartRedraw(subchart_id); } //--- Set the button state to the relevant index if(state) property_button_states[2]=true; else property_button_states[2]=false; //--- Refresh the chart ChartRedraw(); } } //+------------------------------------------------------------------+ //| Enabling/disabling Ask/Bid levels for all chart objects | //+------------------------------------------------------------------+ void ShowAskBid(bool state) { int total_charts =0; // Number of objects long subchart_id =0; // Chart object identifier string chart_name =""; // Chart object name //--- Check if the SubWindow exists // If it exists, then if((subwindow_number=ChartWindowFind(0,subwindow_shortname))>0) { //--- Get the number of chart objects total_charts=ObjectsTotal(0,subwindow_number,OBJ_CHART); //--- Iterate over all chart objects in a loop for(int i=0; i<total_charts; i++) { //--- Get the chart object name chart_name=ObjectName(0,i,subwindow_number,OBJ_CHART); //--- Get the chart object identifier subchart_id=ObjectGetInteger(0,chart_name,OBJPROP_CHART_ID); //--- Set the properties ChartSetInteger(subchart_id,CHART_SHOW_ASK_LINE,state); ChartSetInteger(subchart_id,CHART_SHOW_BID_LINE,state); //--- Refresh the chart object ChartRedraw(subchart_id); } //--- Set the button state to the relevant index if(state) property_button_states[3]=true; else property_button_states[3]=false; //--- Refresh the chart ChartRedraw(); } } //+------------------------------------------------------------------+ //| Enabling/disabling trade levels for all chart objects | //+------------------------------------------------------------------+ void ShowTradeLevels(bool state) { int total_charts =0; // Number of objects long subchart_id =0; // Chart object identifier string chart_name =""; // Chart object name //--- Check if the SubWindow exists // If it exists, then if((subwindow_number=ChartWindowFind(0,subwindow_shortname))>0) { //--- Get the number of chart objects total_charts=ObjectsTotal(0,subwindow_number,OBJ_CHART); //--- Iterate over all chart objects in a loop for(int i=0; i<total_charts; i++) { //--- Get the chart object name chart_name=ObjectName(0,i,subwindow_number,OBJ_CHART); //--- Get the chart object identifier subchart_id=ObjectGetInteger(0,chart_name,OBJPROP_CHART_ID); //--- Set the property ChartSetInteger(subchart_id,CHART_SHOW_TRADE_LEVELS,state); //--- Refresh the chart object ChartRedraw(subchart_id); } //--- Set the button state to the relevant index if(state) property_button_states[4]=true; else property_button_states[4]=false; //--- Refresh the chart ChartRedraw(); } }
これで関数はすべて準備されパネルと連携できます。あとはメイン関数 OnChartEvent() にコードの文字列を1つ追加するだけです。
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- The CHARTEVENT_OBJECT_CLICK event if(ChartEventObjectClick(id,lparam,dparam,sparam)) return; }
インディケータがコンパイルされ、すぐにチャートで実行されれば、関連するタイムフレームボタンがクリックされるときチャートオブジェクトが追加されます。また、プロパティの任意のボタンをクリックすると、チャートオブジェクト内で対応する変更を確認することが可能です。
図5 指定プロパティによるチャートオブジェクトの追加
ただしチャートウィンドウまたはサブウィンドウのサイズが変更されると、チャートオブジェクトサイズは適切に調整されません。ではここから CHARTEVENT_CHART_CHANGE イベントを詳しく考察します。
『グラフィカルオブジェクト』のイベントを追跡するための関数 ChartEventObjectClick() を作成したのと同じように、今度は ChartEventChartChange() 関数を書きます。
//+------------------------------------------------------------------+ //| Event of modifying the chart properties | //+------------------------------------------------------------------+ bool ChartEventChartChange(int id, long lparam, double dparam, string sparam) { //--- Chart has been resized or the chart properties have been modified if(id==CHARTEVENT_CHART_CHANGE) { //--- If the SubWindow has been deleted (or does not exist), while the time frame buttons are clicked, // release all the buttons (reset) if(OnSubwindowDelete()) return(true); //--- Save the height and width values of the main chart and SubWindow, if it exists GetSubwindowWidthAndHeight(); //--- Adjust the sizes of chart objects AdjustChartObjectsSizes(); //--- Refresh the chart and exit ChartRedraw(); return(true); } //--- return(false); }
プログラムがメインチャートサイズまたはプロパティが変更されることを定めているなら、まずSubWindow が削除されたか確認するために OnSubwindowDelete() 関数を使用します。サブウィンドウが見つからなければパネルはリセットされます。
//+------------------------------------------------------------------+ //| Response to Subwindow deletion | //+------------------------------------------------------------------+ bool OnSubwindowDelete() { //--- if there is no SubWindow if(ChartWindowFind(0,subwindow_shortname)<1) { //--- Reset the panel with time frame buttons AddTimeframeButtons(); ChartRedraw(); return(true); } //--- SubWindow exists return(false); }
サブウィンドウがあるべき場所にあれば、サブウィンドウの幅と高さの値が GetSubwindowWidthAndHeight() 関数のグローバル変数に割り当てられます。
//+------------------------------------------------------------------+ //| Saving the SubWindow height and width values | //+------------------------------------------------------------------+ void GetSubwindowWidthAndHeight() { //--- Check if there is a subwindow named SubWindow if((subwindow_number=ChartWindowFind(0,subwindow_shortname))>0) { // Get the subwindow height and width chart_height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,subwindow_number); chart_width=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS,subwindow_number); } }
最終的に AdjustChartObjectsSizes() 関数内でチャートオブジェクトのサイズが調整されます。
//+------------------------------------------------------------------+ //| Adjusting width of chart objects when modifying the window width | //+------------------------------------------------------------------+ void AdjustChartObjectsSizes() { int x_distance =0; // X-coordinate int total_objects =0; // Number of chart objects int chart_object_width =0; // Chart object width string object_name =""; // Object name ENUM_TIMEFRAMES TF =WRONG_VALUE; // Time frame //--- Get the SubWindow number if((subwindow_number=ChartWindowFind(0,subwindow_shortname))>0) { //--- Get the total number of chart objects total_objects=ObjectsTotal(0,subwindow_number,OBJ_CHART); //--- If there are no objects, delete the subwindow and exit if(total_objects==0) { DeleteSubwindow(); return; } //--- Get the width for chart objects chart_object_width=chart_width/total_objects; //--- Iterate over all chart objects in a loop for(int i=total_objects-1; i>=0; i--) { //--- Get the name object_name=ObjectName(0,i,subwindow_number,OBJ_CHART); //--- Set the chart object width and height ObjectSetInteger(0,object_name,OBJPROP_YSIZE,chart_height); ObjectSetInteger(0,object_name,OBJPROP_XSIZE,chart_object_width); //--- Set the chart object position ObjectSetInteger(0,object_name,OBJPROP_YDISTANCE,0); ObjectSetInteger(0,object_name,OBJPROP_XDISTANCE,x_distance); //--- Set the new X-coordinate for the next chart object x_distance+=chart_object_width; } } }
メインチャートのサイズとプロパティを変更するイベントを追跡するためには OnChartEvent() 関数に以下の文字列を追加する必要があります。
インディケータをコンパイルしてチャートにアタッチしたら、メインウィンドウのサイズが変更されるたびにサブウィンドウのサイズに対してチャートオブジェクトが調整されるのを確認することができます。
おわりに
ここで本稿は終わります。宿題として、メインチャート内のシンボルが変更されるとき、チャートオブジェクト内のシンボルが調整される機能を実装してみてください。みなさんはタイムフレームを低い方から高い方へ(左から右へ)連続してチャートオブジェクト内に設定したいとお思いかもしれません。この機能は上述のインディケータバージョンではこれまで実装されていません。
既製のアプリケーション:TF PANELの記述でこれら機能の実装を実演しているビデオを見ることもできます。また本稿にはソースコードファイルが添付されており、それはダウンロード可能ですのでご利用ください。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/749
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索