English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
MQL5におけるトレーディング用コントロールパネルの作成

MQL5におけるトレーディング用コントロールパネルの作成

MetaTrader 5エキスパートアドバイザー | 5 10月 2015, 15:09
2 137 0
Евгений
Евгений

はじめに

効果は、スピードや正確さが命であるトレーダーの作業環境において顕著に現れています。ターミナルの準備中に、分析や市場への参入を速やかに行うために、作業環境をできるかぎり各々にとって快適なもにすることができます。しかし、現実的な問題として、開発者は、すべての人を喜ばせることはできず、特定の人の要望に応えることは不可能です。

例えば、トレーダーにとって、毎秒ごと、「新規注文」の1クリックがとても重要であり、すべてのパラメーターの設定速度がとても緊急性を帯びています。

それでは、どのようなソリューションがあるのでしょうか?ソリューションは、MetaTrader4は、「ボタン」や「編集」、「ラベル」などの素晴らしいコンポーネントを提供しているため、システムの構成をカスタマイズすることができるという部分になります。それでは始めましょう。


2. パネルオプション

まずは、どのような関数がパネルに必要なのかを確認しましょう。主な重点を、パネルを使用しながらのトレーディングに置きます。したがって、以下の関数を内包します。

  • ポジションのオープン
  • 未決注文の発行
  • ポジション/注文の修正
  • ポジションのクロージング
  • 未決注文の削除

また、カラーパネルや、フォントサイズ、保存設定をカスタマイズする機能を付け加えることは何も害はありません。作成予定のパネルのすべての要素の詳しい内容を紹介します。パネルの各関数におけるオブジェクト名、種類、目的の内容を明記します。それぞれのオブジェクト名は「ActP」で始まります。これは、オブジェクトがパネルに属することを意味するものとなっています。


2.1. ポジションのオープン

ポジションのオープンにおいて必要な全パラメーターを以下にて紹介し、ボタンをクリックし実行していきます。ボックスをチェックすることで作動可能な補助線が、ストップロス、利取りレベルの設定において、補助を行います。実行種類の選択がラジオボタンで行われます。

名前 タイプ 概要
ActP_buy_button1 ボタン 買いトレードにおけるボタン
ActP_sell_button1 ボタン 売りトレードのボタン
ActP_DealLines_check1 フラッグ 補助線の設定/リセットフラッグ
ActP_Exe_radio1 ラジオボタン トレードタイプの選択におけるラジオボタン群
ActP_SL_edit1 入力フィールド ストップロスを入力するフィールド
ActP_TP_edit1 入力フィールド 利取りを入力するフィールド
ActP_Lots_edit1 入力フィールド 額を入力するフィールド
ActP_dev_edit1 入力フィールド オープンにおける逸脱の許容範囲の入力フィールド
ActP_mag_edit1 入力フィールド 数値の入力フィールド
ActP_comm_edit1 入力フィールド コメントの入力フィールド

図表1 「トレードのオープン」におけるパネルの要素のリスト


2.2未決注文を発行する

未決注文において必要な全パラメーターを以下にて紹介し、配置していきます。フラッグのチェックを行うことで、使用可能なサポート線は、ストップロスや利取り、ストップ制限レベル、期限切れ時間の設定において役に立ちます。実行の種類や期限切れ日程の選択は、ラジオボタンの選択により実行されます。

名前 タイプ 概要
ActP_buy_button2 ボタン 買い注文ボタン
ActP_sell_button2 ボタン 注文の設定ボタン
ActP_DealLines_check2 フラッグ 設定・リセットフラッグの補助線
ActP_lim_check2 フラッグ オーダーのストップリミットの設定/リセットフラッグ
ActP_Exe_radio2 ラジオボタン 注文の実行種類の選択におけるラジオボタン
ActP_exp_radio2 ラジオボタン 注文の期限切れの種類に関するラジオボタン
ActP_SL_edit2 入力フィールド ストップロスを入力するフィールド
ActP_TP_edit2 入力フィールド 利取りを入力するフィールド
ActP_Lots_edit2 入力フィールド 額を入力するフィールド
ActP_limpr_edit2 入力フィールド ストップ制限オーダーの額の入力フィールド
ActP_mag_edit2 入力フィールド マジックナンバーの入力フィールド
ActP_comm_edit2 入力フィールド コメント用フィールド
ActP_exp_edit2 入力フィールド 期限切れ日程の入力フィールド
ActP_Pr_edit2 入力フィールド 注文実行額の入力フィールド

図表2 「未決注文」パネルの要素のリスト


2.3. トレードの修正/クロージング

トレードのクローズや修正に必要なすべてのパラメーターを紹介します。ボックスのチェックにより使用可能な補助線は、ストップロスや利取りレベルのインストールを手助けします。トレードの選択肢がドロップダウンのリストから生成されます。

名前 タイプ 概要
ActP_ord_button5 ドロップダウンリスト トレードの選択リスト
ActP_mod_button4 ボタン トレード修正ボタン
ActP_del_button4 ボタン トレードのクローズ用ボタン
ActP_DealLines_check4 フラッグ 補助線の設定/リセットフラッグ
ActP_SL_edit4 入力フィールド ストップロスを入力するフィールド
ActP_TP_edit4 入力フィールド 利取りを入力するフィールド
ActP_Lots_edit4 入力フィールド 額を入力するフィールド
ActP_dev_edit4 入力フィールド 許容逸脱範囲の入力フィールド
ActP_mag_edit4 入力フィールド マジックナンバーの表示用フィールド(読み取りのみ)
ActP_Pr_edit4 入力フィールド オープン価格の表示用フィールド(読み取り専用)

図表3. 「トレード修正/クローズにおける要素リスト」パネル


2.4. 注文の修正/削除

未決注文の修正・削除におけるパラメーターを以下にて紹介します。ボックスをチェックすることで使用可能なサポート線が、期限切れ時間、ストップ制限レベル、利取りなどのインストールにて手助けします。期限切れ時間の種類の選択肢は、ラジオボタンとともに生成されます。注文の選択肢は、ドロップダウンリストにて生成されます。

名前 タイプ 概要
ActP_ord_button5 ドロップダウンリスト 注文の選択のためのリスト
ActP_mod_button3 ボタン 注文の修正ボタン
ActP_del_button3 ボタン 注文削除ボタン
ActP_DealLines_check3 フラッグ 補助線の設定/リセットフラッグ
ActP_exp_radio3 ラジオボタン 注文の期限の選択用ラジオボタン
ActP_SL_edit3 入力フィールド ストップロスを入力するフィールド
ActP_TP_edit3 入力フィールド 利取りの入力フィールド
ActP_Lots_edit3 入力フィールド 額の表示用フィールド(読み取り専用)
ActP_limpr_edit3 入力フィールド ストップ制限注文における価格の入力フィールド
ActP_mag_edit3 入力フィールド マジックナンバーの表示用フィールド(読み取り専用)
ActP_comm_edit3 入力フィールド コメント用フィールド
ActP_exp_edit3 入力フィールド 期限切れ時間の入力フィールド
ActP_Pr_edit3 入力フィールド 注文実行価格の入力フィールド
ActP_ticket_edit3 入力フィールド 注文チケット表示用フィールド(読み取り専用)

図表4 「注文の修正/削除の要素リスト」パネル


2.5設定

ボタン、ラベル、テキストの色の選択をドロップダウンリストから行い、様々なフォントサイズの設定を行います。

名前 タイプ 概要
ActP_col1_button6 ドロップダウンリスト ボタンによる色の選択リスト
ActP_col2_button6 ドロップダウンリスト タグの色の選択リスト
ActP_col3_button6 ドロップダウンリスト テキストカラー選択リスト
ActP_font_edit6 入力フィールド テキストサイズ用のフィールド

図表5. 「設定」パネル用リスト

あるボタンが、使用されていないならばパネルの最小化を行うために追加されています。「サポート線」のようなものの存在に気付かれたかもしれません。それらは何で、なぜ必要なのでしょうか?こえらの線の使用により、望ましい額や時間に線を引っ張ってくることで、ストップロスや利取り、未決注文の実行額、ストップ制限注文の額、延期された注文の期限を設定することができます。

結局、視覚的な導入が、テキストによる(主導で額や時間をフィールドに入力する)ものよりもはるかに便利です。また、これらの線は、「選択した注文のパラメータのハイライト」にもなります。たくさんの注文があるため、額を表示する標準のターミナルの線はとてもややこしくなります。


3. インターフェースの作成における一般的なアプローチ

トレードにおいてグラフィカルアシスタントの作成における4つの目標を達成したことになります。この目的のために、ユーザーに優しいインターフェースが必要になります。まず、すべてのコントロールの要素は、ソフトウェアを使用し作成される必要があり、それゆえ、オブジェクトのサイズや位置は事前に計算される必要があります。

それでは、それぞれ重なり合わず見やすいようにオブジェクトの調整を行い終えるなどの長く大変な時間を乗り越え、そしてさらに、新しいオブジェクトを追加する必要があり、全体が新たに再構築される必要があることをイメージしてください、

迅速なアプリ開発環境(Delphi、C++ビルダーなど)に馴染みのある方は、複雑なUIが即刻で作成されるかを知ってることだと思います。

MQL5を使用し、それを実現したいと思います。まず、マウスを使用し、コントロールのオブジェクトを適切な方法で配置し、サイズを調整してください。それから、シンプルなスクリプトを作成します。そのスクリプトはチャート上のすべてのオブジェクトの属性を読み取り、ファイルに記録し、必要であれば、これらの属性を取得し、チャート上に再構築可能です。

そのスクリプトのコードはこのようなものです:

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

input int interfaceID=1; //input parameter - the identifier of the stored interface
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   //Open file for writing  
   int handle=FileOpen("Active_Panel_scheme_"+IntegerToString(interfaceID)+".bin", FILE_WRITE|FILE_BIN);
   if(handle!=INVALID_HANDLE)
     {
      //We will go all the objects on the chart
      for(int i=0;i<ObjectsTotal(0);i++)
        {
         string name=ObjectName(0,i);
         //And write their properties in the file
         FileWriteString(handle,name,100);
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_TYPE));

         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_COLOR));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STYLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_WIDTH));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BACK));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTED));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTABLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_READONLY));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_FONTSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STATE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BGCOLOR));

         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_TEXT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_FONT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,0),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,1),100);

         FileWriteDouble(handle,ObjectGetDouble(0,name,OBJPROP_PRICE));
        }
      //Close file
      FileClose(handle);
      Alert("Done!");
     }
  }
//+------------------------------------------------------------------+

ご覧の通り、コードは極端にシンプルで、すべてのチャートオブジェクトの属性をバイナリファイルに記入します。もっとも重要な点として、ファイルの読み取り中、記録されたプロパティの順番を忘れないことです。

スクリプトの準備が整っているので、インターフェースの作成に向かいます。

まずやらないといけないことは、タブの種類によってインメニューを作成することです。なぜタブが必要なのでしょうか?なぜなら、オブジェクトがたくさんあり、スクリーン上にすべて並べるのは困難だからです。オブジェクトは(図表をご覧の通り)グループ化されているため、個別のタブに各々のグループを配置しやすくなっています。

従って、ターミナルメニューを使用し、Insert -> Objects -> Buttonへと進み、メインメニューとして機能するチャート上部の5つのボタンを作成します。

図1パネルタブ

図1 パネルタブ

オブジェクトは、「Ctrl」キーを押しながら、選択しマウスで引っ張ることで、簡単に複製可能であることを忘れないようにしましょう。これにより、元のオブジェクトを再配置するのではなく、オブジェクトのオブジェクトのコピーを作成します。

「Actp」ですべてのオブジェクトの名前が始まる必要があり、オブジェクト名に特に注意が向けられる必要があります。さらに、「main」をストリング名ににつけることで、メインメニューバーに所属することを示す必要があります。

図 2オブジェクトリスト(パネルタブ)

図 2. オブジェクトリスト(パネルタブ

同様に、タブのコンテンツを新しいチャートに適用してみましょう。各タブのコンテンツは、個別のチャートに位置する必要があります。

「マーケット」タブ:

図 3. 「マーケット」タブの要素

図 3. 「マーケット」タブの要素

「未決注文」タブ:

図4「未決注文」タブの要素

図4「未決注文」タブの要素

設定タブ:

図5「設定」タブの要素

図5. 「設定」タブの要素

最後の「修正/削除」タブは異なっており、 取引の修正だけではなく、未決注文の修正/削除も行います。取引の作業と、注文の作業を二つの個別のサブタブに分けることが合理的だと思われます。まず、扱う注文や取引を選べるようになるドロップダウンリストを使用可能にするボタンを作成しましょう。

図6「修正/削除」タブの要素

図6. 「修正/削除」 タブの要素

その後、サブのタブを作成します。取引を扱う:

図7ポジションを扱う要素

図7. ポジションを扱う要素

注文を扱う:

図8注文を扱うサブのタブ

図8. 注文を扱うサブのタブ

それでは、インターフェースの完成です。

スクリプトを各々のチャートに適用し、個別のファイルでタブを保存します。入力パラメータ「interfaceID」は各々のタブで異なる必要があります。

  • 0 - ホームページ
  • 1 - マーケット
  • 2 - 未決注文
  • 3 - 取引/注文の選択リストのためのボタン
  • 4 - 設定
  • 6 - 取引を扱うサブタブ
  • 7 - 注文を扱うサブタブ

タブ5は、メインメニューの「ウィンドウの最小化」のボタンと一致しているため、何もオブジェクトがありませんので、飛ばします。

すべての操作の後、以下のファイルがterminal -> MQL5 ->のディレクトリ下にて現れます:

図8パネルスキーマのファイルリスト

図9. パネルスキーマのファイルリスト


4. インターフェースの要素のダウンロード

それでは、すべてインターフェースの要素がファイルに格納されたので、作業に移しましょう。まず、パネルの位置を決定しましょう。もし、メインチャート上に異なって配置した場合、価格チャートを邪魔してしまい、不便になります。それゆえ、メインチャートのサブウィンドウにパネルを配置することが合理的です。インジケーターはこのパネルを作成することができます。

それでは作成しましょう:

#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window //place the indicator in a separate window

int OnInit()
  {
//--- indicator buffers mapping
   //Set the short name of the indicator
   IndicatorSetString(INDICATOR_SHORTNAME, "AP");
//---
   return(0);
  }

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
  {
//---
//--- return value of prev_calculated for next call
   return(rates_total);
  }

このインジケーターの主な関数は、様々な計算するのではなく、サブウィンドウの作成であるため、コードはとても簡単です。私たち行う一つのことは、「短い」名前のインジケータをインストールするもので、それによりサブウィンドウを見つけることができます。チャートをコンパイルし、インジケーターに適用すると、ウィンドウが表示されます。

それでは、エキスパートアドバイザーパネルに焦点を当てましょう。新しいエキスパートアドバイザーを作成します。

OnInit ()関数は、以下の演算子を含みます。

double Bid,Ask;         //variables for current prices
datetime time_current;  //time of last tick
int wnd=-1;             //index of the window with an indicator
bool last_loaded=false; //flag indicating whether it's a first initialization or not
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   //Start the timer at intervals of 1 second
   EventSetTimer(1); 
   //Get the latest prices
   get_prices();
   //Define the window with an indicator
   wnd=ChartWindowFind(0,"AP");
   //If the first initialization - create interface
   if(!last_loaded) create_interface();
//---
   return(0);
  }

ここで、タイマーを起動し(理由は以下で紹介します。)、マーケットから最新の価格を取得し、ChartWindowFind関数を用い、インジケーターウィンドウを配置、そして、変数として保存します。Flag last_loaded - はこれがエキスパートアドバイザーの初めての初期化かどうかを指し示します。この情報は、再初期化中にインターフェースを再ローディングするのを避けるために必要なものです。

create_interface ()関数は以下のようになります:

//+------------------------------------------------------------------+
//| Function of the interface creation                               |
//+------------------------------------------------------------------+
void create_interface()
  {
   //if reset settings is selected
   if(Reset_Expert_Settings)
     {
     //Reset
      GlobalVariableDel("ActP_buttons_color");
      GlobalVariableDel("ActP_label_color");
      GlobalVariableDel("ActP_text_color");
      GlobalVariableDel("ActP_font_size");
     }

   //Create the main menu interface
   ApplyScheme(0);
   //Create the interface tab "Market"
   ApplyScheme(1);
   //Set all objects as unmarked
   Objects_Selectable("ActP",false);
   //redraw the chart
   ChartRedraw();
  }

まず、入力パラメーター「リセットの設定」をチェックし、もしインストールされていれば、設定に影響のあるグローバル変数を除去してください。これがどのようにパネルに影響を与えるかについては以下に説明されています。さらにApplyScheme () 関数は、ファイルからインターフェースを作成します。

//+------------------------------------------------------------------+
//| The function for the interface loading                           |
//| ID - ID of the saved interface                                   |
//+------------------------------------------------------------------+
bool ApplyScheme(int ID)
  {
   string fname="Active_Panel_scheme_custom_"+IntegerToString(ID)+".bin";
   //download the standard scheme if there isn't saved scheme 
   if(!FileIsExist(fname)) fname="Active_Panel_scheme_"+IntegerToString(ID)+".bin";
   //open file for reading
   int handle=FileOpen(fname,FILE_READ|FILE_BIN);
   //file opened
   if(handle!=INVALID_HANDLE)
     {
      //Loading all objects
      while(!FileIsEnding(handle))
        {
         string obj_name=FileReadString(handle,100);
         int _wnd=wnd;
         //the auxiliary lines are in the main window
         if(StringFind(obj_name,"line")>=0) _wnd=0;
         ENUM_OBJECT obj_type=FileReadInteger(handle);
         //creating object
         ObjectCreate(0, obj_name, obj_type, _wnd, 0, 0);   
         //and apply the properties 
         ObjectSetInteger(0,obj_name,OBJPROP_XDISTANCE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_YDISTANCE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_XSIZE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_YSIZE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_COLOR,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_STYLE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_WIDTH,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_BACK,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_SELECTED,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_SELECTABLE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_READONLY,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_FONTSIZE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_STATE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,FileReadInteger(handle));

         ObjectSetString(0,obj_name,OBJPROP_TEXT,FileReadString(handle,100));
         ObjectSetString(0,obj_name,OBJPROP_FONT,FileReadString(handle,100));
         ObjectSetString(0,obj_name,OBJPROP_BMPFILE,0,FileReadString(handle,100));
         ObjectSetString(0,obj_name,OBJPROP_BMPFILE,1,FileReadString(handle,100));

         ObjectSetDouble(0,obj_name,OBJPROP_PRICE,FileReadDouble(handle));

         //Set color for the objects
         if(GlobalVariableCheck("ActP_buttons_color") && obj_type==OBJ_BUTTON)
            ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,GlobalVariableGet("ActP_buttons_color"));
         if(GlobalVariableCheck("ActP_label_color") && obj_type==OBJ_LABEL)
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,GlobalVariableGet("ActP_label_color"));
         if(GlobalVariableCheck("ActP_text_color") && (obj_type==OBJ_EDIT || obj_type==OBJ_BUTTON))
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,GlobalVariableGet("ActP_text_color"));
         if(GlobalVariableCheck("ActP_font_size") && (obj_type==OBJ_EDIT || obj_type==OBJ_LABEL))
            ObjectSetInteger(0,obj_name,OBJPROP_FONTSIZE,GlobalVariableGet("ActP_font_size"));
         //Set global variable font size
         if(obj_name=="ActP_font_edit6" && GlobalVariableCheck("ActP_font_size"))
            ObjectSetString(0,obj_name,OBJPROP_TEXT,IntegerToString(GlobalVariableGet("ActP_font_size")));
        }
      //Close file
      FileClose(handle);
      return(true);
     }
   return(false);
  }

これについては複雑な部分は全くありません。その関数は、保存されていたインターフェーススキーマにて指定のファイルを開き、インジケーターウィンドウに作成します。また、オブジェクトの色やフォントサイズをターミナルのグローバル変数から選択します。

Objects_Selectable ()関数は、補助線以外のすべてのオブジェクトをマークされていない状態にし、ボタンのアニメーションをONにし、不必要なオブジェクトの削除を避けるようにします。

//+------------------------------------------------------------------+
//| Function of setting objects as unselectable                      |
//+------------------------------------------------------------------+
void  Objects_Selectable(string IDstr,bool flag)
  {
   //Check all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //If the object belongs to the panel
      if(StringFind(n,IDstr)>=0)
        {
         //Lines remain untouched
         if(!flag)
            if(StringFind(n,"line")>-1) continue; 
         //Set everything unselectable except the lines
         ObjectSetInteger(0,n,OBJPROP_SELECTABLE,flag); 
        }
     }
  }

それではOnTick関数を見てみましょう。最新の市場の価格を取得することができます。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //Get the latest prices
   get_prices();
  }

get_prices()関数は以下の形です:

//+------------------------------------------------------------------+
//| Function obtain information on tick                              |
//+------------------------------------------------------------------+
void get_prices()
  {
   MqlTick tick;
   //if the tick was
   if(SymbolInfoTick(Symbol(),tick))
     {
      //obtain information
      Bid=tick.bid;
      Ask=tick.ask;
      time_current=tick.time;
     }
  }

OnDenit()関数も忘れないようにしましょう:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   //if the deinitialisation reason isn't the timeframe or symbol change
   if(reason!=REASON_CHARTCHANGE)
     {
      //reset initialization flag
      last_loaded=false;  
      //Delete all panel objects
      ObjectsDeleteAll_my("ActP");
      //Delete files with the saved state of the tabs
      FileDelete("Active_Panel_scheme_custom_1.bin");
      FileDelete("Active_Panel_scheme_custom_2.bin");
      FileDelete("Active_Panel_scheme_custom_3.bin");
      FileDelete("Active_Panel_scheme_custom_4.bin");
      FileDelete("Active_Panel_scheme_custom_5.bin");
     }
   //otherwise set a flag
   else last_loaded=true;
   //stop the timer
   EventKillTimer();
  }

まず、ディイニシャライゼーションの原因をチェックします:もしタイムフレームやシンボルの変化のせいであれば、パネルアイテムを削除しません。そのほかのすべてのケースにおいては、アイテムをObjectsDeleteAll_my ()関数を使用し削除します。

//+------------------------------------------------------------------+
//| The function deletes all panel objects                           |
//| IDstr - object identifier                                        |
//+------------------------------------------------------------------+
void  ObjectsDeleteAll_my(string IDstr)
  {
   //check all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //if the name contains the identifier - remove the object
      if(StringFind(n,IDstr)>=0) ObjectDelete(0,n);
     }
  }

エキスパートアドバイザーのコンパイルや起動後、以下の結果を取得します:

図10エキスパートアドバイザーの作業例

図 10. エキスパートアドバイザーの例

しかしながら、これらのオブジェクトが操作に応答するようにできるまで使用はここからの使用はありません。


5. イベントハンドリング

インターフェースが作成されれば、作業に戻ります。オブジェクトにおけるすべてのアクションが特定のイベントを生成します。OnChartEvent function OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)は、ChartEventというイベントのハンドリンングの仕組みになります。 . 以下の興味のあるすべてのイベントを取り扱います:

  • CHARTEVENT_CLICK - チャートへのクリック
  • CHARTEVENT_OBJECT_ENDEDIT - 入力フィールドへの編集を終了する
  • CHARTEVENT_OBJECT_CLICK - グラフィックオブジェクトへのクリック

今回のケースでは、id関数のパラメーターは、イベントのIDを示し、sparamは、このイベントを生成するオブジェクト名にあたります。ほかのすべてのパラメーターは、今回は関係がありません。

まず紹介する最初のイベントは、メインメニューボタンへのクリックになります。


5.1. メインメニューイベントのハンドリング

メインメニューは5つのボタンから構成されるのを思い出してください。もしそれらの一つがクリックされれば、押された状態に変わり、右側のインターフェースへ誘導し、適切なタブをアップロードします。そして、すべてのメニューボタンが押されていない状態に変わります。

//+------------------------------------------------------------------+
//| Event handlers                                                   |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //main menu button click
      if(sparam=="ActP_main_1") {Main_controls_click(1); ChartRedraw(); return;}
       //Here we execute the corresponding operators
      if(sparam=="ActP_main_2") {Main_controls_click(2); ChartRedraw(); return;}
      if(sparam=="ActP_main_3") {Main_controls_click(3); ChartRedraw(); return;}
      if(sparam=="ActP_main_4") {Main_controls_click(4); ChartRedraw(); return;}
      if(sparam=="ActP_main_5") {Main_controls_click(5); ChartRedraw(); return;}
   ...   
   }
...
}

もしメニューボタンにクリックがあれば、Main_controls_click()関数を実行したということです。ChartRedraw()を使用し、チャートを再描画し、関数を完成させましょう。一つのオブジェクトのみ一度にクリックでき、すべてのさらなる実行はCPU時間の浪費につながるので、実行は完成させておきましょう。

//+------------------------------------------------------------------+
//| Tab processor                                                    |
//| ID - index of clicked tab                                        |
//+------------------------------------------------------------------+
void Main_controls_click(int ID)
  {
   int loaded=ID;
   //we will go all tabs
   for(int i=1;i<6;i++)
     {
      //for all except the selected set inactive
      if(i!=ID) 
        {
         //also remember the last active tab
         if(ObjectGetInteger(0,"ActP_main_"+IntegerToString(i),OBJPROP_STATE)==1) loaded=i;
         ObjectSetInteger(0,"ActP_main_"+IntegerToString(i),OBJPROP_STATE,0);
        }
     }
//if(loaded==ID) return;
   //set an active state for the selected
   ObjectSetInteger(0,"ActP_main_"+IntegerToString(ID),OBJPROP_STATE,1);
   //delete the drop-down lists
   DeleteLists("ActP_orders_list_"); 
   DeleteLists("ActP_color_list_");
   //and set the list buttons to the unpressed state
   ObjectSetInteger(0,"ActP_ord_button5",OBJPROP_STATE,0);
   ObjectSetInteger(0,"ActP_col1_button6",OBJPROP_STATE,0);
   ObjectSetInteger(0,"ActP_col2_button6",OBJPROP_STATE,0);
   ObjectSetInteger(0,"ActP_col3_button6",OBJPROP_STATE,0);
   //save state of the last active tab
   SaveScheme(loaded);
   //remove old tab
   DeleteScheme("ActP");
   //and load a new
   ApplyScheme(ID);
   //Set all objects as unselected
   Objects_Selectable("ActP",false);
  }

Objects_Selectable()とApplyScheme()関数を紹介しました。のちにDeleteLists()関数に移ります。

SaveScheme()関数は、インターフェースファイルを保存し、オブジェクトがリロード時にすべての属性を維持します。

//+------------------------------------------------------------------+
//| Interface saving function                                        |
//+------------------------------------------------------------------+
void SaveScheme(int interfaceID)
  {
   //open file for writing
   int handle=FileOpen("Active_Panel_scheme_custom_"+IntegerToString(interfaceID)+".bin",FILE_WRITE|FILE_BIN);
   //if file opened
   if(handle!=INVALID_HANDLE)
     {
      //go all the chart objects
      for(int i=0;i<ObjectsTotal(0);i++)
        {
         string name=ObjectName(0,i);
         //if the object belongs to the panel
         if(StringFind(name,"ActP")<0) continue;
         //and it isn't a tab
         if(StringFind(name,"main")>=0) continue; 
         //write the object properties to a file
         FileWriteString(handle,name,100);
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_TYPE));

         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_COLOR));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STYLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_WIDTH));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BACK));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTED));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTABLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_READONLY));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_FONTSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STATE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BGCOLOR));

         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_TEXT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_FONT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,0),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,1),100);

         FileWriteDouble(handle,ObjectGetDouble(0,name,OBJPROP_PRICE));
        }
      //Close file
      FileClose(handle);
     }
  }

DeleteScheme()関数は、タブのオブジェクトを除去します。

//+------------------------------------------------------------------+
//| Function to delete all the panel objects, except tabs            |
//+------------------------------------------------------------------+
void  DeleteScheme(string IDstr)
  {
   //we will go through all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //remove everything but the tab
      if(StringFind(n,IDstr)>=0 && StringFind(n,"main")<0) ObjectDelete(0,n);
     }
  }

従って、Main_controls_click() 関数を実行することで、古いタブを除去し、前もって保存し、新しいタブをロードします。

エキスパートアドバイザーをコンパイルし、結果を見ます。

それでは、メインメニューボタンをクリックしたのち、新規タブをロードし、オリジナルタブの状態で維持しましょう。

図11「未決注文」タブのアイテム

図11. 「未決注文」タブのアイテム

図12. 「修正/削除」タブの要素

図12. 「修正/クローズ」タブの要素


図12. 「設定」タブの要素

図13. 「設定」タブの要素

これにより、すべての機能を得たので、メインメニューの操作を終了することができます。


5.2. 「フラッグ」コンポーネントイベントのハンドリング

ストップ制限注文や補助線の設定は、「フラッグ」コンポーネントを使用し行われますが、MT5のグラフィカルオブジェクトのリストにはありません。なので、作成してみましょう。「オン」の状態と「オフ」の状態を持つイメージである「グラフィックラベル」オブジェクトがあります。オブジェクトをクリックすることで、その状態が変化します。それぞれの状態に対して個別のイメージがセットされます。各状態に応じてイメージを選んでください:

  • オン
  • オフ

オブジェクトのプロパティに写真を設定します。

図13. 「フラッグ」のプロパティの設定

図13. 「フラッグ」のプロパティの設定

写真がリストで使用可能状態であるために、「Terminal folder-> MQL5-> Images」のフォルダー内に配置され、「Bmp」という拡張子を持つ必要があると覚えておいでください。

オブジェクトをクリックした際に生じるイベントの処理へ移りましょう。トレードのオープン時に、補助線を置く役割を担うフラッグの例を用います。

//click on the flag of the setting of auxiliary lines during transaction opening
      if(sparam=="ActP_DealLines_check1")
      {
         //Check the flag state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //If the flag is set
         if(selected)
         {
            //Retrieve the value of the stop loss and take profit from the input fields
            string SL_txt=ObjectGetString(0, "ActP_SL_edit1", OBJPROP_TEXT);
            string TP_txt=ObjectGetString(0, "ActP_TP_edit1", OBJPROP_TEXT);
            double val_SL, val_TP;
            
            //If the Stop field is not empty
            //save the value
            if(SL_txt!="")
               val_SL=StringToDouble(SL_txt);

            //if empty
            else
            {
               //Take the max. そして、最小値prices of chart
               double pr_max=ChartGetDouble(0, CHART_PRICE_MAX);
               double pr_min=ChartGetDouble(0, CHART_PRICE_MIN);
               //Set the stop at the 1/3 of the chart price range
               val_SL=pr_min+(pr_max-pr_min)*0.33;
            }   
            
            //Similarly processes the Take
            if(TP_txt!="")
               val_TP=StringToDouble(TP_txt);
            else
            {
               double pr_max=ChartGetDouble(0, CHART_PRICE_MAX);
               double pr_min=ChartGetDouble(0, CHART_PRICE_MIN);
               val_TP=pr_max-(pr_max-pr_min)*0.33;
            }      
            //Move the line to new positions
            ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, val_SL);  
            ObjectSetDouble(0, "ActP_TP_line1", OBJPROP_PRICE, val_TP);  
         }
          //If the flag is unset
         else
         {
             //remove the lines
            ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, 0);
            ObjectSetDouble(0, "ActP_TP_line1", OBJPROP_PRICE, 0);
         }
          //redraw the chart
         ChartRedraw();
          //and finish the function 
         return;
      }

同様の方法が、未決注文のクローズ/修正におけるタブの補助線の処理・導入を行うフラッグに使用されています。なので、この記事では詳細には紹介しません。知りたい方はエキスパートアドバイザーのコードを覗いてみてください。

「未決注文」タブのストップ制限注文フラッグの設定は、以下のハンドラーを持ちます。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...

   //Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //Click on the orders stoplimit check box
      if(sparam=="ActP_limit_check2")
      {
         //Check the flag state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected) //if flag is set
         {
            //set the new color for the price edit
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_BGCOLOR, White);
            //enable it for the edit
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_READONLY, false);
            //установим в поле значение текущей цены
            //Set the current price as the field value
            ObjectSetString(0, "ActP_limpr_edit2", OBJPROP_TEXT, DoubleToString(Bid, _Digits));
            //if the auxiliary lines are allowed
            //move them
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetDouble(0, "ActP_lim_line2", OBJPROP_PRICE, Bid);
         }  
          //if flag is unset
         else
         {
            //set the field unavailable for editing
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_BGCOLOR, LavenderBlush);
            //set the field color
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_READONLY, true);
            //and "empty" text
            ObjectSetString(0, "ActP_limpr_edit2", OBJPROP_TEXT, "");
            //if the auxiliary lines are allowed
            //move them to the zero point
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetDouble(0, "ActP_lim_line2", OBJPROP_PRICE, 0);
         }
      }       
   ...   
   }
...
}

フラッグの処理を終了しました。それでは、以下のオブジェクト、「ラジオボタングループ」を見てみましょう。


5.3. 「ラジオボタングループ」のコンポーネントイベントのハンドリング

このコンポーネントを使用し、トレードの種類と注文の期限を選択します。フラッグの場合と同様、グラフィックタブを使用しますが、今回は新しい写真を用います。

  • オン
  • オフ

しかし、クリックしたもの以外のすべてのラジオボタンがリセットされ、停止状態にならなければならないので、問題はとても複雑です。 注文実行におけるラジオボタンの例を考えてみましょう

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on radion button 1 - order execution type
      if(sparam=="ActP_Exe1_radio2")
      {
         //check the radio button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //set the appropriate state
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         //if it selected
         if(selected)
         {
            //reset the other radio buttons
            ObjectSetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE, false);
            //redraw the chart
            ChartRedraw();
            //finish the execution of function
            return;
         }
         //redraw the chart
         ChartRedraw();
         //finish the execution of function
         return;
      }
 
       //Similarly for the radio button 2
      if(sparam=="ActP_Exe2_radio2") 
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         if(selected)
         {
            ObjectSetInteger(0, "ActP_Exe1_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE, false);
            ChartRedraw();
            return;
         }
         ChartRedraw();
         return;
      }   

       //Similarly for the radio button 3
      if(sparam=="ActP_Exe3_radio2") 
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         if(selected)
         {
            ObjectSetInteger(0, "ActP_Exe1_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE, false);
            ChartRedraw();
            return;
         }
         ChartRedraw();
         return;
      }     
   ...   
   }
...
}

注文の期限ラジオボタンは、3番目をクリックした際、追加のステップを実行しなければいけないという点が異なっており、注文の期限の設定をする必要があります。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //Click on the 3rd radio button - order expiration date
      if(sparam=="ActP_exp3_radio2")
      {
         //checking it state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         //if it selected
         if(selected)
         {
            //reset the remained radio buttons
            ObjectSetInteger(0, "ActP_exp1_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_exp2_radio2", OBJPROP_STATE, false);
            //set the new date to the date edit field
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_BGCOLOR, White);
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_READONLY, false);
            ObjectSetString(0, "ActP_exp_edit2", OBJPROP_TEXT, TimeToString(time_current));
            //if auxiliary lines are allowed 
            //set the new time line
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetInteger(0, "ActP_exp_line2", OBJPROP_TIME, time_current);
            ChartRedraw();
            return;
         }

          //if it isn't selected
         else
         {
            //set the edit field as not available for editing
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_BGCOLOR, LavenderBlush);
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_READONLY, true);
            //remove the auxiliary line
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetInteger(0, "ActP_exp_line2", OBJPROP_TIME, 0);
         }      
         ChartRedraw();
         return;
   ...   
   }
...
}

ラジオボタンの作業はこれにて終了です。


5.4. ドロップダウンリストのイベントの作成とハンドリング

注文の選択や修正する取引の選択、クロージング、削除やカラー選択パネルなどのためにドロップダウンリストを使用します。取引 / 注文のリストから始めましょう。

「修正/クローズ」タブに現れる最初のものは、「Select an order -->」と書かれたボタンです。これは、リストを稼働させるボタンになります。クリックすると、ドラップダウンリストが展開され、選択を行った後、しまわれます。 このボタンのハンドラーCHARTEVENT_OBJECT_CLICKを見てみましょう:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on the drop-down list activate button (order select)

      if(sparam=="ActP_ord_button5")
      {
         //check status
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //the list is activated
         if(selected)// the list is selected
         {
            //delete interface
            DeleteScheme("ActP", true);
            //arrays for serving the information about the orders
            string info[100];
            //array for the tickets
            int tickets[100];
            //initialize it
            ArrayInitialize(tickets, -1);
            //get orders info
            get_ord_info(info, tickets);
            //create the list
            create_list(info, tickets);
         }
          //the list isn't active
         else
         {
            //delete it
            DeleteLists("ActP_orders_list_");
         }
          //redraw the chart
         ChartRedraw();
          //finish the function
         return;
      }      
   ...   
   }
...
}

私たちの主要なゴールは、取引・注文がマーケット上にあるのか、そうであれば、情報を抽出し、リスト上に表示することです。get_ord_info()関数がこの役割を果たします:

//+------------------------------------------------------------------+
//| The function for obtaining the information about orders          |
//+------------------------------------------------------------------+
void get_ord_info(string &info[],int &tickets[])
  {
   //initialize the counter
   int cnt=0;
   string inf;
   //if there is an open position
   if(PositionSelect(Symbol()))
     {
     //combine all order infomation in a single line
      double vol=PositionGetDouble(POSITION_VOLUME);
      int typ=PositionGetInteger(POSITION_TYPE);
      if(typ==POSITION_TYPE_BUY) inf+="BUY ";
      if(typ==POSITION_TYPE_SELL) inf+="SELL ";
      inf+=DoubleToString(vol, MathCeil(MathAbs(MathLog(vol)/MathLog(10))))+" lots";
      inf+=" at "+DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN), Digits());
      //write the results
      info[cnt]=inf;
      tickets[cnt]=0;
      //increment the counter
      cnt++;
     }

   //all orders
   for(int i=0;i<OrdersTotal();i++)
     {
      //get ticket
      int ticket=OrderGetTicket(i);
      //if order symbol is equal to chart symbol
      if(OrderGetString(ORDER_SYMBOL)==Symbol())
        {
         //combine all order infomation in a single line
         inf="#"+IntegerToString(ticket)+" ";
         int typ=OrderGetInteger(ORDER_TYPE);
         double vol=OrderGetDouble(ORDER_VOLUME_CURRENT);
         if(typ==ORDER_TYPE_BUY_LIMIT) inf+="BUY LIMIT ";
         if(typ==ORDER_TYPE_SELL_LIMIT) inf+="SELL LIMIT ";
         if(typ==ORDER_TYPE_BUY_STOP) inf+="BUY STOP ";
         if(typ==ORDER_TYPE_SELL_STOP) inf+="SELL STOP ";
         if(typ==ORDER_TYPE_BUY_STOP_LIMIT) inf+="BUY STOP LIMIT ";
         if(typ==ORDER_TYPE_SELL_STOP_LIMIT) inf+="SELL STOP LIMIT ";
         inf+=DoubleToString(vol, MathCeil(MathAbs(MathLog(vol)/MathLog(10))))+" lots";
         inf+=" at "+DoubleToString(OrderGetDouble(ORDER_PRICE_OPEN), Digits());
         //write the results
         info[cnt]=inf;
         tickets[cnt]=ticket;
         //increment the counter
         cnt++;
        }
     }
  }

注文チケットや取引の情報を一つのブロックに組み込みます。

さらに、create_list()関数はこの情報に基づきリストを作成します。

//+------------------------------------------------------------------+
//| The function creates list of positions                           |
//| info - array for the positions                                   |
//| tickets - array for the tickets                                  |
//+------------------------------------------------------------------+
void create_list(string &info[],int &tickets[])
  {
   //get the coordinates of the list activation button
   int x=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_XDISTANCE);
   int y=ObjectGetInteger(0, "ActP_ord_button5", OBJPROP_YDISTANCE)+ObjectGetInteger(0, "ActP_ord_button5", OBJPROP_YSIZE);
   //get colors
   color col=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_COLOR);
   color bgcol=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_BGCOLOR);
   //get window height
   int wnd_height=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,wnd);
   int y_cnt=0;
   //proceed arrays
   for(int i=0;i<100;i++)
     {
      //break if end reached
      if(tickets[i]==-1) break;
      //calculate the list item coordinates
      int y_pos=y+y_cnt*20;
      //if the windiow limits are reachedl, start a new column
      if(y_pos+20>wnd_height) {x+=300; y_cnt=0;}
      y_pos=y+y_cnt*20;
      y_cnt++;
      string name="ActP_orders_list_"+IntegerToString(i)+" $"+IntegerToString(tickets[i]);
      //create element
      create_button(name,info[i],x,y_pos,300,20);
      //and set its properties
      ObjectSetInteger(0,name,OBJPROP_COLOR,col);
      ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0);
      ObjectSetInteger(0,name,OBJPROP_STATE,0);
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);
      ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bgcol);
     }
  }

そして最後に、DeleteLists()関数がリストの要素を除去します。

//+------------------------------------------------------------------+
//| The function for the list deletion                               |
//+------------------------------------------------------------------+
void  DeleteLists(string IDstr)
  {
   //proceed all objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //delete lists
      if(StringFind(n,IDstr)>=0 && StringFind(n,"main")<0) ObjectDelete(0,n);
     }
  }

それでは、ボタンをクリックするとリストが作成されます。リスト上の要素への各クリックにより特定のアクションが発生します。特に、注文を扱うインターフェースのローディングや、注文/取引に関する情報の挿入などです。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   // Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //Click not on an item of order selection list
      if(StringFind(sparam, "ActP_orders_list_")<0)
      {
          //Remove it
         DeleteLists("ActP_orders_list_");
          //Set the activation button to "unpressed"
         ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0);
          //redraw chart
         ChartRedraw();
      }     
       //Click on the order selection list item
      else
      {
          //Set a new name for the activation button
         ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, ObjectGetString(0, sparam, OBJPROP_TEXT));
          //Set the activation button to "unpressed"
         ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0);
          //get ticket from the list item description
         int ticket=StringToInteger(StringSubstr(sparam, StringFind(sparam, "$")+1));
          //Load the interface
         SetScheme(ticket);
          //and delete the list
         DeleteLists("ActP_orders_list_");
          //chart redraw
         ChartRedraw();
      }
   ...   
   }
...
}

これが複雑な部分になります。リストのサイズやオブジェクト名を前もって知らないため、リストの要素名を取得し情報を得る必要があります。SetScheme()関数は、取引や未決注文を扱う適切なインターフェースを作成します。

//+------------------------------------------------------------------+
//| The function sets the interface depending on type:               |
//| position or pending order                                        |
//| t - ticket                                                       |
//+------------------------------------------------------------------+
void SetScheme(int t)
  {
   //if position
   if(t==0)
     {
      //check for its presence
      if(PositionSelect(Symbol()))
        {
         //delete old scheme
         DeleteScheme("ActP",true);
         //and apply new
         ApplyScheme(6);
         //set position parameters
         SetPositionParams();
         //the objects are unavailable for the selection
         Objects_Selectable("ActP",false);
        }
     }
   //if order
   if(t>0)
     {
      //check for its presence
      if(OrderSelect(t))
        {
         //delete old scheme
         DeleteScheme("ActP",true);
         //and apply new
         ApplyScheme(7);
         //set order parameters
         SetOrderParams(t);
         //the objects are unavailable for the selection
         Objects_Selectable("ActP",false);
        }
     }
  }

SetPositionParams()とSetOrderParams()関数は、ロードされたインターフェースの必要なプロパティをインストールします。

//+------------------------------------------------------------------+
//| Set position parameters for the objects                          |
//+------------------------------------------------------------------+
void SetPositionParams()
  {
   //if position is exists
   if(PositionSelect(Symbol()))
     {
      //get its parameters
      double pr=PositionGetDouble(POSITION_PRICE_OPEN);
      double lots=PositionGetDouble(POSITION_VOLUME);
      double sl=PositionGetDouble(POSITION_SL);
      double tp=PositionGetDouble(POSITION_TP);
      double mag=PositionGetInteger(POSITION_MAGIC);
      //and set new values to the objects
      ObjectSetString(0,"ActP_Pr_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(pr)));
      ObjectSetString(0,"ActP_lots_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(lots)));
      ObjectSetString(0,"ActP_SL_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(sl)));
      ObjectSetString(0,"ActP_TP_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(tp)));
      if(mag!=0) ObjectSetString(0,"ActP_mag_edit4",OBJPROP_TEXT,IntegerToString(mag));
      //redraw chart
      ChartRedraw();
     }
   //if there isn't position, show message 
   else MessageBox("There isn't open position for "+Symbol());
  }
//+------------------------------------------------------------------+
//| Set pending order parameters for the objects                     |
//| ticket - order ticket                                            |
//+------------------------------------------------------------------+
void SetOrderParams(int ticket)
  {
   //if order exists
   if(OrderSelect(ticket) && OrderGetString(ORDER_SYMBOL)==Symbol())
     {
      //get its parameters
      double pr=OrderGetDouble(ORDER_PRICE_OPEN);
      double lots=OrderGetDouble(ORDER_VOLUME_CURRENT);
      double sl=OrderGetDouble(ORDER_SL);
      double tp=OrderGetDouble(ORDER_TP);
      double mag=OrderGetInteger(ORDER_MAGIC);
      double lim=OrderGetDouble(ORDER_PRICE_STOPLIMIT);
      datetime expir=OrderGetInteger(ORDER_TIME_EXPIRATION);
      ENUM_ORDER_TYPE type=OrderGetInteger(ORDER_TYPE);
      ENUM_ORDER_TYPE_TIME expir_type=OrderGetInteger(ORDER_TYPE_TIME);
      
      //of order type is stoplimit, modify the interface
      if(type==ORDER_TYPE_BUY_STOP_LIMIT || type==ORDER_TYPE_SELL_STOP_LIMIT)
        {
         //set new value to the order price edit
         ObjectSetString(0,"ActP_limpr_edit3",OBJPROP_TEXT,DoubleToString(lim,_Digits));
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_BGCOLOR,White);
         //set order price available for edit
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_READONLY,false);
        }
      //if order type isn't stoplimit, modify the interface
      else
        {
         ObjectSetString(0,"ActP_limpr_edit3",OBJPROP_TEXT,"");
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_BGCOLOR,LavenderBlush);
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_READONLY,true);
        }

      //check expiration type
      //and set interface elements
      switch(expir_type)
        {
         case ORDER_TIME_GTC:
           {
            ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,1);
            ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,0);
            break;
           }
         case ORDER_TIME_DAY:
           {
            ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,1);
            ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,0);
            break;
           }
         case ORDER_TIME_SPECIFIED:
           {
            ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,1);
            //in addition, set new value to the edit
            ObjectSetString(0,"ActP_exp_edit3",OBJPROP_TEXT,TimeToString(expir));
            break;
           }
        }
      //set new values for the objects
      ObjectSetString(0,"ActP_Pr_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(pr)));
      ObjectSetString(0,"ActP_lots_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(lots)));
      ObjectSetString(0,"ActP_SL_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(sl)));
      ObjectSetString(0,"ActP_TP_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(tp)));
      ObjectSetString(0,"ActP_ticket_edit3",OBJPROP_TEXT,IntegerToString(ticket));
      if(mag!=0) ObjectSetString(0,"ActP_mag_edit3",OBJPROP_TEXT,IntegerToString(mag));
      ChartRedraw();
     }
   //if there isn't such order, show message
   else MessageBox("There isn't an order with ticket "+IntegerToString(ticket)+" for "+Symbol());
  }

最後に、リストは、チャートをクリックした際に、CHARTEVENT_CLICKを使用し、すべて除去される必要があります。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - is click on the chart
   if(id==CHARTEVENT_CLICK)
   {
       //delete all lists
      DeleteLists("ActP_orders_list_");
      DeleteLists("ActP_color_list_"); 
       //Set the activate buttons to the unpressed state
      ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0);
      ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0);
      ObjectSetInteger(0, "ActP_col2_button6", OBJPROP_STATE, 0);
      ObjectSetInteger(0, "ActP_col3_button6", OBJPROP_STATE, 0);
      ChartRedraw(); 
      return;
   }
...
}

結果的に、9のドロップダウンリストがあります。

図14. 「修正/クローズ」のドロップダウンリストパネルの例

図14. 「修正/クローズ」のドロップダウンリストパネルの例

色の選択のリストを設定タブに作成する必要があります。

活動状態ボタンのハンドラを考えてみましょう。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //Click on the button to activate the colors drop-down list
      if(sparam=="ActP_col1_button6")
      {
          //check state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //the list is active
         if(selected)//the list is active
         {
             //creat list
            create_color_list(100, "ActP_col1_button6", 1);
             //Set the position of the remaining buttons to "unpressed"
            ObjectSetInteger(0, "ActP_col2_button6", OBJPROP_STATE, 0);
            ObjectSetInteger(0, "ActP_col3_button6", OBJPROP_STATE, 0);
             //delete other lists
            DeleteLists("ActP_color_list_2");
            DeleteLists("ActP_color_list_3");                 
         }
          //the list isn't selected
         else
         {
             //delete it
            DeleteLists("ActP_color_list_");
         }
          //redraw chart
         ChartRedraw();
          //finish the execution of function
         return;
      }
   ...   
   }
...
}

ここでは、注文選択リスト時と同様の以下の方法を使用します。

リストの作成関数は異なります。

//+------------------------------------------------------------------+
//| Function for creating the colors list                            |
//| y_max - maximal list widthа                                      |
//| ID - list ID                                                     |
//| num - interface number                                           |
//+------------------------------------------------------------------+
void create_color_list(int y_max,string ID,int num)
  {
  //Get the coordinates of the list activation button
   int x=ObjectGetInteger(0,ID,OBJPROP_XDISTANCE);
   int y=ObjectGetInteger(0, ID, OBJPROP_YDISTANCE)+ObjectGetInteger(0, ID, OBJPROP_YSIZE);
   //get color
   color col=ObjectGetInteger(0,ID,OBJPROP_COLOR);
   //and window width
   int wnd_height=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,wnd);
   y_max+=y;
   int y_cnt=0;
   //We will go through the colors array
   for(int i=0;i<132;i++)
     {
      color bgcol=colors[i];
      //calculate list item coordinates
      int y_pos=y+y_cnt*20;
      //if we reached the boundaries of the window, start new column
      if(y_pos+20>wnd_height || y_pos+20>y_max) {x+=20; y_cnt=0;}
      y_pos=y+y_cnt*20;
      y_cnt++;
      //create new element
      string name="ActP_color_list_"+IntegerToString(num)+ID+IntegerToString(i);
      create_button(name,"",x,y_pos,20,20);
      //and set its properties
      ObjectSetInteger(0,name,OBJPROP_COLOR,col);
      ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0);
      ObjectSetInteger(0,name,OBJPROP_STATE,0);
      ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bgcol);
     }
  }

さらに、リスト要素におけるクリックの処理を行います。

,void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click isn't on the color list button
      if(StringFind(sparam, "ActP_color_list_1")<0)
      {
          //delete list
         DeleteLists("ActP_color_list_1");
          //set color list activation button to "unpressed"
         ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0);
          //redraw chart
         ChartRedraw();
      }     
       //click on the color list
      else
      {
          //get color from the list
         color col=ObjectGetInteger(0, sparam, OBJPROP_BGCOLOR);
          //set it for all the buttons
         SetButtonsColor(col);
          //set button to unpressed
         ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0);
          //delete list
         DeleteLists("ActP_color_list_1");
          //redraw chart
         ChartRedraw();
      }
   ...   
   }
...
}

SetButtonsColor()関数は、ボタンの色を設定します。

//+------------------------------------------------------------------+
//| The function sets color for all buttons                          |
//| col - color                                                      |
//+------------------------------------------------------------------+
void SetButtonsColor(color col)
  {
   //We will go through all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //If the object belongs to the panel and its has a button type
      //set color
      if(StringFind(n,"ActP")>=0 && ObjectGetInteger(0,n,OBJPROP_TYPE)==OBJ_BUTTON) 
         ObjectSetInteger(0,n,OBJPROP_BGCOLOR,col); 
     }
   //set global variable
   GlobalVariableSet("ActP_buttons_color",col);
  }

結果を以下にて見てください:

図15. ボタンの色の設定

図15. ボタンの色の設定

色の選択リストとテキストラベルは類似しています。結果として、数回のクリックでパネルをカラフルにできます。

図16. 変更後のパネル、ボタン、テキストの色

図16. 変更後のパネル、ボタン、テキストの色

リストについてはこれで終了です。入力フィールドに進みましょう。


5.5. エントリーフィールドEventのハンドリング

エントリーフィールドは、CHARTEVENT_OBJECT_ENDEDITというイベントを生成します。これは、フィールドのテキストの編集の完成時に発生します。このイベントを扱う必要性の唯一の理由は、エントリーフィールドの価格に関連し、価格の補助線の設定を行うためです。

ストップ線の例を考えてみましょう。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //End edit event
   if(id==CHARTEVENT_OBJECT_ENDEDIT)//end edit event
   {
   ...
      //if edit field is SL field
      if(sparam=="ActP_SL_edit1")
      {
        //and auxiliary lines are enabled
         if(ObjectGetInteger(0,"ActP_DealLines_check1",OBJPROP_STATE)==1)
         {
            //get text from the field
            double sl_val=StringToDouble(ObjectGetString(0, "ActP_SL_edit1", OBJPROP_TEXT));
            //move lines at new position
            ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, sl_val);
         }
         //redraw chart
         ChartRedraw();
         //it ins't necessary to proceed the other objects, because the event from the one
         return;
      }
   ...   
   }
...
}

その他のエントリーフィールドは同様に処理されます。


5.6タイマーイベントのハンドリング

タイマーは、補助線の監視に使用されます。このように、線を動かす際に、接続された価格の値が自動的に入力フィールドに挿入されます。タイマーの各ティックにてOnTimer()関数が実行されます。

「マーケット」タブでのストップロスと利取り線の配置の例を考えてみましょう。

void OnTimer()// Timer handler
{
   //panel 1 is active
   if(ObjectGetInteger(0, "ActP_main_1", OBJPROP_STATE)==1)
   {  
      //if auxiliary lines are allowed
      if(ObjectGetInteger(0,"ActP_DealLines_check1",OBJPROP_STATE)==1)
      {
         //set new values to the edit fields
         double sl_pr=NormalizeDouble(ObjectGetDouble(0, "ActP_SL_line1", OBJPROP_PRICE), _Digits);
         //stop loss
         ObjectSetString(0, "ActP_SL_edit1", OBJPROP_TEXT, DoubleToString(sl_pr, _Digits));
         //take profit
         double tp_pr=NormalizeDouble(ObjectGetDouble(0, "ActP_TP_line1", OBJPROP_PRICE), _Digits);
         ObjectSetString(0, "ActP_TP_edit1", OBJPROP_TEXT, DoubleToString(tp_pr, _Digits));
      }   
   }
   ...
   //redraw chart
   ChartRedraw();
}
//+------------------------------------------------------------------+

その他の線の追跡は同様に実行されます。


6. トレード処理の実行

この時点で、すべての必要なエントリーフィールド、チェックボックス、線、ラジオボタンの記入を終えました。それでは、所有するすべてのデータをもとにトレーディングを行っていきましょう。


6.1. 取引の開始

「市場から」タブは、「買い」と「売り」のボタンを持っています。もしすべてのフィールドが正しく記入されていれば、トレードがボタンのクリック時に実行されます。

これらのボタンのハンドラーを見てみましょう。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the object on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the Buy button
      if(sparam=="ActP_buy_button1")
      {
          //check its state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if it "pressed"
         if(selected)
         {
             //try to perform a deal
            deal(ORDER_TYPE_BUY);
             //and set the button to the unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }

          //redraw chart
         ChartRedraw();
          //and finish the function execution
         return;
      }
      //******************************************
       //the similar for the sell button
      if(sparam=="ActP_sell_button1")
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected)
         {
            deal(ORDER_TYPE_SELL);
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
         ChartRedraw();
         return;
      }     
   ...   
   }
...
}

deal()関数が作動してることがわかります。

//+------------------------------------------------------------------+
//| Deal function                                                    |
//+------------------------------------------------------------------+
int deal(ENUM_ORDER_TYPE typ)
  {
   //get the data from the objects
   double SL=StringToDouble(ObjectGetString(0,"ActP_SL_edit1",OBJPROP_TEXT));
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit1", OBJPROP_TEXT));
   double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit1",OBJPROP_TEXT));
   int mag=StringToInteger(ObjectGetString(0, "ActP_Magic_edit1", OBJPROP_TEXT));
   int dev=StringToInteger(ObjectGetString(0, "ActP_Dev_edit1", OBJPROP_TEXT));
   string comm=ObjectGetString(0,"ActP_Comm_edit1",OBJPROP_TEXT);
   ENUM_ORDER_TYPE_FILLING filling=ORDER_FILLING_FOK;
   if(ObjectGetInteger(0,"ActP_Exe2_radio1",OBJPROP_STATE)==1) filling=ORDER_FILLING_IOC;
   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res;
   req.action=TRADE_ACTION_DEAL;
   req.symbol=Symbol();
   req.volume=lots;
   req.price=Ask;
   req.sl=NormalizeDouble(SL, Digits());
   req.tp=NormalizeDouble(TP, Digits());
   req.deviation=dev;
   req.type=typ;
   req.type_filling=filling;
   req.magic=mag;
   req.comment=comm;
   //send order
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

特に難しい点はありません。オブジェクトからの必要な情報を読み取り、それをもとに、トレードリクエストを発行します。

見てみましょう:

図17. トレーディング処理 - 買い取引の実行結果

図17. トレーディング処理 - 買い取引の実行結果

ご覧の通り、買い取引は、成功したようです。


6.2. 未決注文の設定

「未決注文」タブの「買い」と「売り」ボタンは、未決注文の発行に必要なものです。

ハンドラーを見てみましょう:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the pending order set button
      if(sparam=="ActP_buy_button2")
      {
         //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //if it pressed
         if(selected)
         {
            ENUM_ORDER_TYPE typ; 
            //get the pending order from the edit
            double pr=NormalizeDouble(StringToDouble(ObjectGetString(0, "ActP_Pr_edit2", OBJPROP_TEXT)), Digits());
            //if it isn't stoplimit order
            if(ObjectGetInteger(0, "ActP_limit_check2", OBJPROP_STATE)==0)
            {
               //if the order price is below the current price, set limit order
               if(Ask>pr) typ=ORDER_TYPE_BUY_LIMIT; 
               //overwise - stop order
               else typ=ORDER_TYPE_BUY_STOP;
            }
              //if stoplimit order is specified
            else
            {
               //set operation type
               typ=ORDER_TYPE_BUY_STOP_LIMIT;
            }   
              //try to place order
            order(typ);
              //set button to the unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //and finish the execution of function
         return;
      }     
      //******************************************  

       //similar for the sell pending order
      if(sparam=="ActP_sell_button2")
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected)
         {
            ENUM_ORDER_TYPE typ;
            double pr=NormalizeDouble(StringToDouble(ObjectGetString(0, "ActP_Pr_edit2", OBJPROP_TEXT)), Digits());
            if(ObjectGetInteger(0, "ActP_limit_check2", OBJPROP_STATE)==0)
            {
               if(Bid<pr) typ=ORDER_TYPE_SELL_LIMIT;
               else typ=ORDER_TYPE_SELL_STOP;
            }
            else
            {
               typ=ORDER_TYPE_SELL_STOP_LIMIT;
            }   
            order(typ);
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
         ChartRedraw();
         return;
      }        
   ...   
   }
...
}

ここでは、設定した価格と現在の市場価格との関係に基づき、先物の注文の種類を決定します。その後、order()関数が注文を決定します。

//+------------------------------------------------------------------+
//| The function places an order                                     |
//+------------------------------------------------------------------+
int order(ENUM_ORDER_TYPE typ)
  {
   //get the order details from the objects
   double pr=StringToDouble(ObjectGetString(0,"ActP_Pr_edit2",OBJPROP_TEXT));
   double stoplim=StringToDouble(ObjectGetString(0,"ActP_limpr_edit2",OBJPROP_TEXT));
   double SL=StringToDouble(ObjectGetString(0, "ActP_SL_edit2", OBJPROP_TEXT));
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit2", OBJPROP_TEXT));
   double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit2",OBJPROP_TEXT));
   datetime expir=StringToTime(ObjectGetString(0,"ActP_exp_edit2",OBJPROP_TEXT));
   int mag=StringToInteger(ObjectGetString(0,"ActP_Magic_edit2",OBJPROP_TEXT));
   string comm=ObjectGetString(0,"ActP_Comm_edit2",OBJPROP_TEXT);
   ENUM_ORDER_TYPE_FILLING filling=ORDER_FILLING_FOK;
   if(ObjectGetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE)==1) filling=ORDER_FILLING_IOC;
   if(ObjectGetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE)==1) filling=ORDER_FILLING_RETURN;
   ENUM_ORDER_TYPE_TIME expir_type=ORDER_TIME_GTC;
   if(ObjectGetInteger(0, "ActP_exp2_radio2", OBJPROP_STATE)==1) expir_type=ORDER_TIME_DAY;
   if(ObjectGetInteger(0, "ActP_exp3_radio2", OBJPROP_STATE)==1) expir_type=ORDER_TIME_SPECIFIED;

   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res; 
   req.action=TRADE_ACTION_PENDING;
   req.symbol=Symbol();
   req.volume=lots;
   req.price=NormalizeDouble(pr,Digits());
   req.stoplimit=NormalizeDouble(stoplim,Digits());
   req.sl=NormalizeDouble(SL, Digits());
   req.tp=NormalizeDouble(TP, Digits());
   req.type=typ;
   req.type_filling=filling;
   req.type_time=expir_type;
   req.expiration=expir;
   req.comment=comm;
   req.magic=mag;
   //place order
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

見てみましょう:

図18. トレーディング処理 - 未決注文結果

図18. トレーディング処理 - 未決注文結果

買いのストップリミットがうまく設定されたようです。


6.3. ポジションの修正

「修正/クローズ」タブの編集ボタンは、選択したポジションの修正を行います。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the graphic object on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the modify position button
      if(sparam=="ActP_mod_button4")
      {
          //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if it pressed
         if(selected)//if pressed
         {
            //modify position
            modify_pos();
            //delete the elements of the scheme
            DeleteScheme("ActP" ,true);
            //and reset it (update the interface)
            SetScheme(0);
            //set the button to the unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //finish the execution of function
         return;
      }     
   ...   
   }
...
}

Modify_pos()関数は修正を直接行う関数です。

//+------------------------------------------------------------------+
//| The function modifies the position parameters                    |
//+------------------------------------------------------------------+
int modify_pos()
  {
   if(!PositionSelect(Symbol())) MessageBox("There isn't open position for symbol "+Symbol(),"Message");
   //get the details from the edit objects
   double SL=StringToDouble(ObjectGetString(0,"ActP_SL_edit4",OBJPROP_TEXT));  
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit4", OBJPROP_TEXT));
   int dev=StringToInteger(ObjectGetString(0,"ActP_dev_edit4",OBJPROP_TEXT));
   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res;  
   req.action=TRADE_ACTION_SLTP;
   req.symbol=Symbol();
   req.sl=NormalizeDouble(SL, _Digits);
   req.tp=NormalizeDouble(TP, _Digits);
   req.deviation=dev;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

結果:

図19. トレーディング処理 - 取引のプロパティの修正の結果(TPとSL)

図19. トレーディング処理 - 取引のプロパティの修正結果(TPとSL)

ストップロスと利取りレベルの変更が成功しました。


6.4. ポジションのクローズ

「修正/クローズ」タブのクローズボタンは、ポジションのクローズに使用されます。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on the close button
      if(sparam=="ActP_del_button4")
      {
         //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if pressed
         if(selected)
         {
            //try to close position
            int retcode=close_pos();
            //if successful
            if(retcode==10009)
            {
               //delete scheme elements
               DeleteScheme("ActP" ,true);
               //set the new text for the list activisation
               ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, "Select order -->");
            }
            //set button state to unpressed
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //finish the execution of function
         return;
      }     
   ...   
   }
...
}

close_pos()関数はクローズを行います。

//+------------------------------------------------------------------+
//| Closes the position                                              |
//+------------------------------------------------------------------+
int close_pos()
  {
   if(!PositionSelect(Symbol())) MessageBox("There isn't open position for symbol "+Symbol(),"Message");
   //get the position details from the objects
   double lots=StringToDouble(ObjectGetString(0,"ActP_lots_edit4",OBJPROP_TEXT));
   if(lots>PositionGetDouble(POSITION_VOLUME)) lots=PositionGetDouble(POSITION_VOLUME);
   int dev=StringToInteger(ObjectGetString(0, "ActP_dev_edit4", OBJPROP_TEXT));
   int mag=StringToInteger(ObjectGetString(0, "ActP_mag_edit4", OBJPROP_TEXT));

   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res;

   //the opposite deal is dependent on position type
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
     {
      req.price=Bid;
      req.type=ORDER_TYPE_SELL;
     }
   else
     {
      req.price=Ask;
      req.type=ORDER_TYPE_BUY;
     }

   req.action=TRADE_ACTION_DEAL;
   req.symbol=Symbol();
   req.volume=lots;
   req.sl=0;
   req.tp=0;
   req.deviation=dev;
   req.type_filling=ORDER_FILLING_FOK;
   req.magic=mag;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

結果、選択した取引の3つのうち1.5ロットがクローズしました。

図20. トレーディング - 部分的なポジションのクローズ

図 20. トレーディング - 部分的なポジションのクローズ


6.5. 未決注文の修正

「修正/クローズ」タブの編集ボタンは、選択した注文の修正を行います。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on the order modify button
      if(sparam=="ActP_mod_button3")
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected)
         {     
            //get the order ticket from the edit
            string button_name=ObjectGetString(0, "ActP_ord_button5", OBJPROP_TEXT);
            long ticket=StringToInteger(StringSubstr(button_name, 1, StringFind(button_name, " ")-1));
            //modifying an order
            modify_order(ticket);
            //update interface
            DeleteScheme("ActP" ,true);
            SetScheme(ticket);
            //set button to unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //and finish the execution of function
         return;
      }     
   ...   
   }
...
}

Modify_order ()関数は直接の修正に用いられます。

//+------------------------------------------------------------------+
//| The function modifies an order                                   |
//| ticket - order ticket                                            |
//+------------------------------------------------------------------+
int modify_order(int ticket)
  {
   //get the order details from the corresponding chart objects
   double pr=StringToDouble(ObjectGetString(0,"ActP_Pr_edit3",OBJPROP_TEXT)); 
   double stoplim=StringToDouble(ObjectGetString(0,"ActP_limpr_edit3",OBJPROP_TEXT));
   double SL=StringToDouble(ObjectGetString(0, "ActP_SL_edit3", OBJPROP_TEXT));
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit3", OBJPROP_TEXT));
   double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit3",OBJPROP_TEXT));
   datetime expir=StringToTime(ObjectGetString(0,"ActP_exp_edit3",OBJPROP_TEXT));
   ENUM_ORDER_TYPE_TIME expir_type=ORDER_TIME_GTC;
   if(ObjectGetInteger(0, "ActP_exp2_radio3", OBJPROP_STATE)==1) expir_type=ORDER_TIME_DAY;
   if(ObjectGetInteger(0, "ActP_exp3_radio3", OBJPROP_STATE)==1) expir_type=ORDER_TIME_SPECIFIED;

   //prepare request to modify
   MqlTradeRequest req;
   MqlTradeResult res;
   req.action=TRADE_ACTION_MODIFY;
   req.order=ticket;
   req.volume=lots;
   req.price=NormalizeDouble(pr,Digits());
   req.stoplimit=NormalizeDouble(stoplim,Digits());
   req.sl=NormalizeDouble(SL, Digits());
   req.tp=NormalizeDouble(TP, Digits());
   req.type_time=expir_type;
   req.expiration=expir;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

それでは、結果を見てみましょう。注文の修正が成功しています:

図21. 未決注文の修正

図21. 未決注文の修正


6.6. 未決注文の削除

「修正/クローズ」タブの削除ボタンは、選択した注文の削除を担っています。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the graphic object on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the order delete button
      if(sparam=="ActP_del_button3")
      {
         //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if pressed
         if(selected)
         {
            //get the ticket from the list
            string button_name=ObjectGetString(0, "ActP_ord_button5", OBJPROP_TEXT);
            long ticket=StringToInteger(StringSubstr(button_name, 1, StringFind(button_name, " ")-1));
            //try to delete order
            int retcode=del_order(ticket);
            //if successful
            if(retcode==10009)
            {
               //delete all objects of the scheme
               DeleteScheme("ActP" ,true);
               //set new text for the list activation button
               ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, "Select an order -->");
            }
             //set button state to unpressed
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //and finish the execution of function
         return;
      }     
   ...   
   }
...
}

del_order()関数は、注文の削除を行います。

//+------------------------------------------------------------------+
//| The function for pending order deletion                          |
//| ticket - order ticket                                            |
//+------------------------------------------------------------------+
int del_order(int ticket)
  {
   //prepare request for deletion
   MqlTradeRequest req;
   MqlTradeResult res;
   req.action=TRADE_ACTION_REMOVE;
   req.order=ticket;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

結果をみてみましょう - 注文は削除されています。

図22. トレーディング - 未決注文の削除

図22 トレーディング処理 - 未決注文の削除


結論

パネルのすべての関数がテストされ、正常に作動しました。

この記事を読んで得られる知識がマーケットにて代替不能な救済であるコントロールパネルの開発に役立つことを願っています。

パネルを始めるためには、アーカイブをターミナルのフォルダーに展開する必要があり、 AP インンジケーターをチャートに適用し、それからやっと アクティブパネル エキスパートアドバイザーを起動してください。

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

添付されたファイル |
MQL5でICQを用いたExpert Advisorの連携 MQL5でICQを用いたExpert Advisorの連携
本稿は、Expert Advisor と ICQ ユーザー間の情報交換について述べていきます。いくつかの例を提供します。ICQ クライアントを使用し、携帯電話やPDAでクライアント端末から遠隔でトレーディング情報を受け取りたい方には興味を引かれる資料を提供することとなるでしょう。
トレードレポートとSMS通知の作成と発行 トレードレポートとSMS通知の作成と発行
トレーダーはかならずしも何時間もトレーディング端末の前に座っていられるわけでも、またそうしたいわけでもありません。特に、トレーディングシステムが多かれ少なかれ形式化され自動でマーケット状況を特定できる場合にはそうです。本稿ではトレード結果レポートをHTMLファイルとして作成し、FTPを介してWWWサーバーにアップロードする方法について述べます。また、トレードイベント通知をSMSとして携帯電話に送信することみついても考察します。
利益のある取引戦略を見つける方法 利益のある取引戦略を見つける方法
この記事では、『ニューラルネットワークを利用しつつ、履歴データ上でコンピュータを使って取引戦略を作り上げることは可能か?』という問いについての答えを与えます。
異なる国での異なるタイムゾーンに基づくトレーディング戦略例 異なる国での異なるタイムゾーンに基づくトレーディング戦略例
インターネット検索をしていると、多くの戦略を見つけるのはたやすいことです。そこから多様な提案を得ることができます。インサイダーの方法を採り入れ、異なる大陸の異なるタイムゾーンに基づく戦略作成の手順を見ていきます。