
MQL5におけるトレーディング用コントロールパネルの作成
はじめに
効果は、スピードや正確さが命であるトレーダーの作業環境において顕著に現れています。ターミナルの準備中に、分析や市場への参入を速やかに行うために、作業環境をできるかぎり各々にとって快適なもにすることができます。しかし、現実的な問題として、開発者は、すべての人を喜ばせることはできず、特定の人の要望に応えることは不可能です。
例えば、トレーダーにとって、毎秒ごと、「新規注文」の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 パネルタブ
オブジェクトは、「Ctrl」キーを押しながら、選択しマウスで引っ張ることで、簡単に複製可能であることを忘れないようにしましょう。これにより、元のオブジェクトを再配置するのではなく、オブジェクトのオブジェクトのコピーを作成します。
「Actp」ですべてのオブジェクトの名前が始まる必要があり、オブジェクト名に特に注意が向けられる必要があります。さらに、「main」をストリング名ににつけることで、メインメニューバーに所属することを示す必要があります。
図 2. オブジェクトリスト(パネルタブ
同様に、タブのコンテンツを新しいチャートに適用してみましょう。各タブのコンテンツは、個別のチャートに位置する必要があります。
「マーケット」タブ:
図 3. 「マーケット」タブの要素
「未決注文」タブ:図4「未決注文」タブの要素
設定タブ:
図5. 「設定」タブの要素
最後の「修正/削除」タブは異なっており、 取引の修正だけではなく、未決注文の修正/削除も行います。取引の作業と、注文の作業を二つの個別のサブタブに分けることが合理的だと思われます。まず、扱う注文や取引を選べるようになるドロップダウンリストを使用可能にするボタンを作成しましょう。
図6. 「修正/削除」 タブの要素
その後、サブのタブを作成します。取引を扱う:
図7. ポジションを扱う要素
注文を扱う:
図8. 注文を扱うサブのタブ
それでは、インターフェースの完成です。
スクリプトを各々のチャートに適用し、個別のファイルでタブを保存します。入力パラメータ「interfaceID」は各々のタブで異なる必要があります。
- 0 - ホームページ
- 1 - マーケット
- 2 - 未決注文
- 3 - 取引/注文の選択リストのためのボタン
- 4 - 設定
- 6 - 取引を扱うサブタブ
- 7 - 注文を扱うサブタブ
タブ5は、メインメニューの「ウィンドウの最小化」のボタンと一致しているため、何もオブジェクトがありませんので、飛ばします。
すべての操作の後、以下のファイルがterminal -> MQL5 ->のディレクトリ下にて現れます:
図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. エキスパートアドバイザーの例
しかしながら、これらのオブジェクトが操作に応答するようにできるまで使用はここからの使用はありません。
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. 「未決注文」タブのアイテム
図12. 「修正/クローズ」タブの要素
図13. 「設定」タブの要素
これにより、すべての機能を得たので、メインメニューの操作を終了することができます。
5.2. 「フラッグ」コンポーネントイベントのハンドリング
ストップ制限注文や補助線の設定は、「フラッグ」コンポーネントを使用し行われますが、MT5のグラフィカルオブジェクトのリストにはありません。なので、作成してみましょう。「オン」の状態と「オフ」の状態を持つイメージである「グラフィックラベル」オブジェクトがあります。オブジェクトをクリックすることで、その状態が変化します。それぞれの状態に対して個別のイメージがセットされます。各状態に応じてイメージを選んでください:
- オン
- オフ
オブジェクトのプロパティに写真を設定します。
図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. 「修正/クローズ」のドロップダウンリストパネルの例
色の選択のリストを設定タブに作成する必要があります。
活動状態ボタンのハンドラを考えてみましょう。
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. ボタンの色の設定
色の選択リストとテキストラベルは類似しています。結果として、数回のクリックでパネルをカラフルにできます。
図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. トレーディング処理 - 買い取引の実行結果
ご覧の通り、買い取引は、成功したようです。
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. トレーディング処理 - 未決注文結果
買いのストップリミットがうまく設定されたようです。
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)
ストップロスと利取りレベルの変更が成功しました。
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. トレーディング - 部分的なポジションのクローズ
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. 未決注文の修正
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 トレーディング処理 - 未決注文の削除
結論
パネルのすべての関数がテストされ、正常に作動しました。
この記事を読んで得られる知識がマーケットにて代替不能な救済であるコントロールパネルの開発に役立つことを願っています。
パネルを始めるためには、アーカイブをターミナルのフォルダーに展開する必要があり、 AP インンジケーターをチャートに適用し、それからやっと アクティブパネル エキスパートアドバイザーを起動してください。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/62




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