English Русский Deutsch
preview
Controlsクラスを使用してインタラクティブなMQL5ダッシュボード/パネルを作成する方法(第2回):ボタンの応答性の追加

Controlsクラスを使用してインタラクティブなMQL5ダッシュボード/パネルを作成する方法(第2回):ボタンの応答性の追加

MetaTrader 5トレーディング | 23 1月 2025, 09:31
314 0
Allan Munene Mutiiria
Allan Munene Mutiiria

はじめに

前回の記事では、MetaQuotes Language 5のダッシュボードパネルの主要コンポーネントを正常にセットアップしました。この段階では、ボタンやラベルは静的な状態で、基礎的ながらも非アクティブな構造にとどまっていました。今回は単なる視覚的なフレームワークを超え、パネルを真にインタラクティブなものに進化させます。ユーザーの入力やクリックに応答する機能を加えることで、パネルをリアルタイムの取引に対応した動的なツールに変えていきます。

この記事(事実上、第2回)では、第1回で作成したボタンの機能を自動化し、クリック時や編集時に適切に反応する方法を解説します。特定のアクションをトリガーするイベントの設定方法を学び、ユーザーがパネルをより有意義に操作できるようにします。以下の主要なトピックを取り上げます。

  1. 自動化される要素の解説:機能が付加されるコンポーネントについての詳細な説明
  2. MQL5でのGUIインタラクションの自動化::ボタンがユーザー入力やクリックに効果的に反応するために必要なコードの実装
  3. 結論:インタラクティブなダッシュボードパネルの構築における進捗の要約

それでは、これらのトピックを深掘りし、取引インターフェイスをさらに進化させましょう。


自動化される要素の解説

この記事では、MQL5パネルの第1回で作成したボタンの自動化に集中します。それぞれのボタンには特定の機能が割り当てられており、ユーザーのコマンドに直感的に反応するようにする必要があります。この応答性は、単にバックグラウンドで動作するプログラムとは異なり、取引パネルがユーザーフレンドリーでアクセスしやすいものであるために必須の要素です。まず、パネルの右上隅に、インターフェイス全体を閉じるために設計されたボタンがあります。したがって、MetaTrader 5チャートで取引環境が開いている場合は、アプリケーションを閉じるのと同じ方法でパネルを閉じることができるはずです。

また、取引ボタンがアクティブな間は、特定の取引操作を実行するボタンを設置します。これには、[Open Buy]、[Sell]、[Sell Stop]、[Sell Limit]、[Buy Stop]、[Buy Limit]といったボタンが含まれます。これらのボタンを使用することで、注文を迅速に行い、変化の激しい市場に即時対応できるようになります。さらに、取引管理のための「クローズボタン」も自動化します。これには、[Close All]、[Close All Profit Trades]、そして一部のユーザーが操作に緊張するような [Close All Pending Orders]が含まれます。これらのボタンは、クリックすることでその機能に応じた操作を実行します。

最後に、口座情報や背景情報を詳しく表示する「情報ボタン」を自動化します。このボタンを押すと、トレーダーが口座の詳細を把握し、より良い意思決定ができるような情報画面が表示されます。これらの要素全体の目的は、トレーダーが必要とする操作を簡単にし、また、従来のパネルよりもユーザーエンゲージメントを向上させるレスポンシブな取引パネルを作成することにあります。

これらの自動化プロセスとコンポーネントを理解しやすくするために、以下にこれまでの進捗を振り返りながら、詳細を説明します。

コンポーネントのイラスト

何をするのか理解できたところで、さっそく自動化に取り掛かりましょう。まだ前回の記事を読んでいない場合は、GUI要素の静的アセンブリを作成した記事をご覧ください。そうすることで、私たちの進め方に確実についてくることができます。それでは、始めましょう。


MQL5のGUIインタラクションの自動化

単純なプロセスから複雑なプロセスへと、時系列に沿った構成を進めていきます。そのため、ティックや相場変更ごとに口座情報を更新する仕組みを構築します。この目的を達成するために使用するのが、OnTickイベントハンドラです。これは、価格が変動したときに自動的に呼び出されるMQL5の組み込み関数です。この関数データ型で、実行を直接処理し、出力を返す必要はありません。関数の実装は以下のようになります。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

  ...
}
//+------------------------------------------------------------------+

これは価格の更新をおこなうイベントハンドラであり、ロジックの中心となります。以下のように、この関数に制御ロジックを追加します。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   //--- Start of the OnTick function, called on every price tick

   //--- Check if the background color of the INFO button is yellow
   if (obj_Btn_INFO.ColorBackground() == clrYellow) {
      //--- Update the account equity display on the panel
      obj_Btn_ACC_EQUITY.Text(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2));

      //--- Update the account balance display on the panel
      obj_Btn_ACC_BALANCE.Text(DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2));

      //--- Update the server trade time display on the panel
      obj_Btn_TIME.Text(TimeToString(TimeTradeServer(), TIME_DATE | TIME_SECONDS));
   }

   //--- End of the OnTick function
}
//+------------------------------------------------------------------+

OnTick関数内で最初に実行するアクションは、情報ボタン(obj_Btn_INFO)の背景色を確認することです。ボタンの背景が黄色に設定されている場合、これは情報表示モードが有効化されていることを意味します。この条件を満たした場合、パネル上の口座関連の各種情報を更新します。具体的には、まずAccountInfoDouble関数を使用して現在の口座エクイティを取得します。この際、プロパティ「ACCOUNT_EQUITY」を入力パラメータとして渡し、DoubleToString関数を用いて値を小数点以下2桁にフォーマットします。そして、このフォーマットされた値をobj_Btn_ACC_EQUITYボタンのテキストに割り当てることで、常に最新のエクイティ情報を表示できる状態にします。

次に、同様の手順で口座残高を更新します。プロパティ「ACCOUNT_BALANCE」を使って口座の残高を取得し、値を小数点以下2桁にフォーマットした上で、obj_Btn_ACC_BALANCEボタンのテキストに設定します。最後に、サーバー取引時間の表示を更新します。TimeTradeServer関数を使用して現在のサーバー取引時間を取得し、この値を日付と秒が含まれる形式にフォーマットします。その後、このフォーマット済みの値をobj_Btn_TIMEボタンのテキストに設定します。このようにして、取引シナリオにおいてタイムリーな意思決定に欠かせない最新の取引時間を常に把握できるようにします。以下が結果です。

ONTICKアップデート

視覚化を通じて、時間フィールドが適切に更新され、成功していることが確認できます。これで、最初の自動化コンポーネントが完成しました。簡単だったでしょう。次にGUIパネルの他のコンポーネントに進みます。残りの要素の自動化はOnChartEvent関数ハンドラ内でおこなわれるので、その入力パラメータと関数について深く見てみましょう。

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

この関数の目的は、ユーザーまたはMQL5プログラムによるチャートの変更を処理することです。したがって、マウスを動かしたり、ボタンフィールドを編集したり、ラベルやボタンをクリックしたりといったユーザーのインタラクションは、このイベントハンドラによって捕捉され、処理されます。その論点を分解して解釈してみましょう。

  • id: このパラメータはイベントIDを表し、11の定義済みイベントタイプのうちの1つに対応します。これらのイベントには、キーの押下、マウスの移動、オブジェクトの作成、チャートの変更、カスタムイベントなどが含まれます。カスタムイベントでは、CHARTEVENT_CUSTOMからCHARTEVENT_CUSTOM_LASTまでのIDを使用できます。イベントの種類は以下の11種類です。

チャートイベントタイプ

  • lparam: :long型のイベントパラメータ。その値は、処理される特定のイベントに依存します。例えば、キー押下イベント中のキーコードを表すことができます。
  • dparam: double型のイベントパラメータ。lparamと同様、その値はイベントタイプによって異なります。例えば、マウスの移動イベント中、マウスカーソルの位置を伝えます。
  • sparam: 文字列型のイベントパラメータ。繰り返しになりますが、その意味はイベントによって異なります。例えば、オブジェクトの作成時に、新しく作成されたオブジェクトの名前を保持することができます。

よりわかりやすく示すために、関数内部で、操作ログへの4つの引数すべてを出力してみましょう。

// Print the 4 function parameters    
Print("ID = ",id,", LPARAM = ",lparam,", DPARAM = ",dparam,", SPARAM = ",sparam);

この関数は、チャートイベントID、long型のイベント値、double型のイベント値、string型の値を表示します。参照しやすいように次のGIFを見てみましょう。

一般的なチャートイベント

GIFからすべてが明らかになったはずです。次に、GUIパネル要素のチャートクリックイベントのキャプチャに移ります。したがって、IDはCHARTEVENT_OBJECT_CLICKになります。

   //Print("ID = ",id,", LPARAM = ",lparam,", DPARAM = ",dparam,", SPARAM = ",sparam);

   if (id==CHARTEVENT_OBJECT_CLICK){
        
        //---
   }

無関係な情報で操作ログをスパムしたくないので、まず前の行をコメントアウトします。2つのスラッシュ(//)はシングルラインコメントと呼ばれ、行頭から行末までコードをコメントアウトします。コメントは、特に実行中にコンピュータによって無視されます。オブジェクトがクリックされたかどうかを確認するために、if文を使用します。これは、チャートイベントIDをオブジェクトクリックの列挙値と比較することで実現します。オブジェクトがクリックされた場合、引数を表示してどのような値が得られるか確認しましょう。以下のコードを使用します。 

   if (id==CHARTEVENT_OBJECT_CLICK){
      Print("ID = ",id,", LM = ",lparam,", DM = ",dparam,", SPARAM = ",sparam);

     ...
   }

printout関数では、LPARAMをLPに、DPARAMをDPに変更するだけで、チャートイベントIDとクリックされたオブジェクトの名前だけに集中できるようになります。以下はそのロジックの図解です。

オブジェクトクリックGIF

自動化する最初のボタンは取引ボタンです。 

      if (sparam==obj_Btn_TRADE.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_TRADE.Name());

         // Reset the pressed states of all buttons to ensure only one button appears pressed at a time
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Change the background color of the Trade button to yellow to indicate it is active
         obj_Btn_TRADE.ColorBackground(clrYellow);
         // Set the background color of the Close and Info buttons to silver to indicate they are inactive
         obj_Btn_CLOSE.ColorBackground(clrSilver);
         obj_Btn_INFO.ColorBackground(clrSilver);

         // Set the border color of the Trade button to yellow to match its background
         obj_Btn_TRADE.ColorBorder(clrYellow);
         // Set the border color of the Close and Info buttons to silver to indicate they are inactive
         obj_Btn_CLOSE.ColorBorder(clrSilver);
         obj_Btn_INFO.ColorBorder(clrSilver);

         // Call a function to destroy the Close section if it exists
         destroySection_Close();
         // Call a function to destroy the Information section if it exists
         destroySection_Information();

         // Create the Trade section, bringing it to the forefront
         createSection_Trade();
      }

ここでは、Name関数を使用して文字列パラメータが[Trade]ボタンの名前と一致するかどうかを確認することで、[Trade]ボタンがクリックされたときのインタラクションを管理します。まず、Print関数を使用して、クリックされたオブジェクトの名前をログに記録します。これにより、どのボタンがクリックされたのかを確認でき、デバッグが容易になります。次に、各ボタン(obj_Btn_TRADE、obj_Btn_CLOSE、obj_Btn_INFO)について、Pressed関数を呼び出し、falseフラグを渡して押下状態をリセットします。これにより、視覚的に押されているボタンが同時に1つだけになるよう制御します。リセット後、ColorBackground関数を使用して[Trade]ボタンの背景色を黄色に変更し、[Close]ボタンと[Info]ボタンの背景色を銀色に設定することで、[Trade]ボタンがアクティブであることを視覚的に示します。同様に、ColorBorder関数を使用して境界線の色も更新します。[Trade]ボタンには黄色を、その他のボタンには銀色を設定します。

次に、インターフェースをクリーンアップするために、destroySection_Close関数およびdestroySection_Information関数を呼び出して、[Close]セクションや[Information]セクションの表示コンテンツを削除します。最後に、createSection_Trade関数を呼び出して取引セクションを動的に作成・表示し、ユーザーが取引インターフェイスにアクセスできるようにします。同様の手順を[Close]ボタンにも適用します。

      // Check if the clicked object is the Close button
      else if (sparam==obj_Btn_CLOSE.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_CLOSE.Name());

         // Reset the pressed states of all buttons
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Set the background color of the Trade button to silver, indicating it's inactive
         obj_Btn_TRADE.ColorBackground(clrSilver);
         // Change the background color of the Close button to yellow to indicate it is active
         obj_Btn_CLOSE.ColorBackground(clrYellow);
         obj_Btn_INFO.ColorBackground(clrSilver);

         // Set the border color of the Trade button to silver
         obj_Btn_TRADE.ColorBorder(clrSilver);
         // Set the border color of the Close button to yellow
         obj_Btn_CLOSE.ColorBorder(clrYellow);
         obj_Btn_INFO.ColorBorder(clrSilver);

         // Call a function to destroy the Trade section if it exists
         destroySection_Trade();
         // Call a function to destroy the Information section if it exists
         destroySection_Information();

         // Create the Close section, bringing it to the forefront
         createSection_Close();
      }

ここでは、[Trade]ボタンの場合とほぼ同じロジックを実行します。[Name]関数を使用して、文字列パラメータが[Close]ボタンの名前と一致するかどうかを確認することで、[Close]ボタンがクリックされるシナリオを処理します。まず、Print関数を使用してクリックされたボタンの名前をログに記録します。これにより、正しいボタンがクリックされたことをデバッグの目的で確認できます。次に、Pressed関数を使用して、obj_Btn_TRADE、obj_Btn_CLOSE、obj_Btn_INFOボタンの押下状態をリセットします。これにより、クリック後にボタンが視覚的に押されたままにならないよう制御します。次に、ColorBackground関数を使用して[Close]ボタンの背景色を黄色に変更してアクティブであることを視覚的に示すと同時に、[Trade]ボタンと[Info]ボタンの背景色を銀色に設定して非アクティブであることを示します。

同様に、ColorBorder関数を使用してボタンの境界線の色を調整します。[Close]ボタンの境界線を黄色に設定して背景に一致させる一方で、[Trade]ボタンと[Info]ボタンの境界線を銀色に設定し、非アクティブであることを示します。インターフェイスをクリーンアップするために、destroySection_Trade関数およびdestroySection_Information関数を呼び出して、取引セクションや情報セクションの既存コンテンツを削除します。最後に、createSection_Close関数を呼び出し、ポジションや注文のクローズに関連するインターフェイスを動的に生成・表示して、ユーザーがパネル上の適切なクローズオプションを操作できるようにします。なお、情報ボタンにも同じロジックが採用されています。コードスニペットは以下の通りです。

      // Check if the clicked object is the Information button
      else if (sparam==obj_Btn_INFO.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_INFO.Name());

         // Reset the pressed states of all buttons
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Set the background color of the Trade and Close buttons to silver, indicating they are inactive
         obj_Btn_TRADE.ColorBackground(clrSilver);
         obj_Btn_CLOSE.ColorBackground(clrSilver);
         // Change the background color of the Info button to yellow to indicate it is active
         obj_Btn_INFO.ColorBackground(clrYellow);

         // Set the border color of the Trade and Close buttons to silver
         obj_Btn_TRADE.ColorBorder(clrSilver);
         obj_Btn_CLOSE.ColorBorder(clrSilver);
         // Set the border color of the Info button to yellow
         obj_Btn_INFO.ColorBorder(clrYellow);

         // Call a function to destroy the Trade section if it exists
         destroySection_Trade();
         // Call a function to destroy the Close section if it exists
         destroySection_Close();

         // Create the Information section, bringing it to the forefront
         createSection_Information();
      }

プログラムをコンパイルして実行すると、コントロールボタンの操作に関する次の出力が得られます。

コントロールクリック

成功したことがわかります。ここで、Windowsボタンがクリックされたときにパネルを破棄するようにします。

      // Check if the clicked object is the exit button (X button)
      else if (sparam==obj_Btn_X.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_X.Name());

         // Call functions to destroy all sections, effectively closing the entire panel
         destroySection_Trade();
         destroySection_Close();
         destroySection_Information();

         // Call a function to destroy the main panel itself
         destroySection_Main_Panel();
      }

ここでは、Name関数を使用して、文字列パラメータが終了ボタンの名前と一致するかどうかを確認することで、終了ボタンがクリックされた場合の処理をおこないます。まず、Print関数を使用してクリックされたオブジェクトの名前をデバッグ目的で記録します。これにより、終了ボタンが確実にクリックされたことを確認できます。

次に、いくつかの関数を呼び出してパネル全体を閉じます。まず、destroySection_Trade、destroySection_Close、destroySection_Information関数を呼び出し、現在表示されている場合に取引セクション、ポジションクローズセクション、またはアカウント情報セクションを削除します。これらの関数により、パネル内のすべてのインタラクティブセクションが適切にクリアされます。最後に、メインパネル自体を破棄する役割を持つdestroySection_Main_Panel関数を呼び出します。これにより、パネルのすべての要素がインターフェイスから完全に削除され、パネルが閉じられます。以下にイラストを示します。

パネルを閉じる

ここでは、それぞれのボタンがクリックされた際に取引操作を処理する必要があります。その処理を簡単におこなうためには、プロセスを支援するクラスインスタンスを含める必要があります。そのため、ソースコードの冒頭で#includeを使用して取引インスタンスをインクルードします。これにより、取引オブジェクトの作成に使用するCTradeクラスにアクセスできるようになります。取引操作に必要なので、これは非常に重要です。

#include <Trade/Trade.mqh>
CTrade obj_Trade;

プリプロセッサは、#include <Trade/Trade.mqh>行をファイルTrade.mqhの内容に置き換えます。角括弧は、Trade.mqhファイルが標準ディレクトリ(通常、terminal_installation_directory\MQL5\Include)から取得されることを示します。カレントディレクトリは検索に含まれません。この行はプログラム中のどこにでも配置できますが、通常は、より良いコード構造と参照を容易にするために、すべてのインクルージョンはソースコードの先頭に置かれます。CTradeクラスのobj_Tradeオブジェクトを宣言すると、MQL5開発者のおかげで、そのクラスに含まれるメソッドに簡単にアクセスできるようになります。

CTRADEクラス

取引ライブラリを組み込んだら、売りボタンがクリックされたときに売りポジションを開くロジックを開始できます。ここで採用する必要があるロジックは次のとおりです。

      else if (sparam==obj_Btn_SELL.Name()){ //--- Check if the Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELL.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
         double entry_price = Bid; //--- Set the entry price for selling to the current bid price
         double stopLoss = Ask+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
         double takeprofit = Ask-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
         
         Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
         obj_Trade.Sell(lots,_Symbol,entry_price,stopLoss,takeprofit); //--- Execute the sell order
      }

ここでは、売りボタンがクリックされたことを確認したら、デバッグの目的でPrint関数を使用してボタンクリックイベントをログに記録し、クリックされたボタンの名前を出力します。その後、重要な取引の詳細を収集します。まず、市場価格を取得するSymbolInfoDouble関数を使用して銘柄の現在のask価格とbid価格を取得し、NormalizeDouble関数を用いてこれらの価格を正規化します。これにより、価格が_Digitsで定義された小数点以下の桁数に従って適切にフォーマットされることを保証します。

次に、StringToDouble関数を使用して[Lots]フィールドに入力されたテキストを数値に変換し、ロットサイズを取得します。売り注文では実行価格としてbid価格が使用されるため、エントリー価格を現在のbid価格に設定します。また、ユーザー入力に基づいてストップロスとテイクプロフィットの値を計算します。ストップロスは、ask価格に[SL]入力フィールドから取得したユーザー定義値を加え、それに銘柄の最小価格変動単位_Pointを考慮して調整することで算出します。同様に、テイクプロフィットは[TP]入力フィールドの値をask価格から差し引くことで計算します。

最後に、Print関数を用いて、ロットサイズ、エントリー価格、ストップロス、テイクプロフィットなどの注文詳細をログに記録します。売り注文は、obj_TradeオブジェクトのSell関数を呼び出し、ロットサイズ、銘柄、エントリー価格、ストップロス、テイクプロフィットといった必要なパラメータを渡すことで実行されます。買いポジションの場合も、以下に示すようにほぼ同じロジックが使用されます。

      else if (sparam==obj_Btn_BUY.Name()){ //--- Check if the Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUY.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
         double entry_price = Ask; //--- Set the entry price for buying to the current ask price
         double stopLoss = Bid-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
         double takeprofit = Bid+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
         
         Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
         obj_Trade.Buy(lots,_Symbol,entry_price,stopLoss,takeprofit); //--- Execute the buy order
      }

コンパイルして進行状況をテストすると、次の出力が得られます。

売り買いボタン

リミット注文とストップ注文を開くには、同様のロジックが使用されます。ただし、これらは現在の市場相場を直接使用しないため、セキュリティチェック用の追加アルゴリズムを設定して組み込む必要があります。まずは売りストップボタンから始めましょう。

      else if (sparam==obj_Btn_SELLSTOP.Name()){ //--- Check if the Sell Stop button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELLSTOP.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Bid - stopslevel*_Point; //--- Calculate the valid price for placing a sell stop order
         
         if (user_price > valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," > ",valid_price); //--- Log an error message
         }
         else if (user_price <= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the sell stop order
            double stopLoss = user_price+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.SellStop(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the sell stop order
         }
      }

ここでは、Name関数を使用して、変数文字列パラメータが[Sell Stop]ボタンの名前と一致するかどうかを確認することで、[Sell Stop]ボタンがクリックされた場合の自動化ロジックを処理します。この確認後、通常どおりPrint 関数を使用してイベントをログに記録し、デバッグの目的でクリックされたボタンの名前を出力します。まず、SymbolInfoDouble関数を使用して市場価格を取得し、NormalizeDoubleを用いて_Digitsで定義された銘柄の小数点精度に従ってフォーマットすることで、ask価格とbid価格を取得・正規化します。

次に、[Price]入力フィールドから「ユーザー定義の価格」を取得し、StringToDouble関数を使用してテキストデータを数値データに変換します。さらに、SymbolInfoInteger関数とSYMBOL_TRADE_STOPS_LEVELパラメータを使用して、ストップ注文の検証に必要な銘柄の最小ストップレベルを取得します。有効な価格は、bid価格からストップレベル(ポイント単位に変換済み)を差し引いて計算されます。

次に、計算した有効価格と「ユーザー定義の価格」を比較して、その価格が有効かどうかを確認します。ユーザー価格が有効範囲を超えている場合は、「無効なストップ価格」というエラーメッセージをログに記録します。一方、ユーザー定義の価格が有効であれば、次のステップに進みます。StringToDoubleを使用して入力フィールドからロットサイズを取得し、数値データに変換します。エントリー価格はユーザー定義の価格に設定されます。次に、[SL]フィールドから取得したユーザー定義のストップロス値をエントリー価格に加算し、銘柄の価格増分(_Point)に基づいて調整することでストップロスを計算します。同様に、[TP]フィールドから取得したユーザー定義のテイクプロフィット値をエントリー価格から減算してテイクプロフィットを計算します。

最後に、Print関数を使用して、ロットサイズ、「エントリー価格」、「ストップロス」、「テイクプロフィット」などの注文詳細をログに記録します。売りストップ注文は、obj_TradeオブジェクトのSell Stop関数を呼び出し、ロットサイズ、エントリー価格、銘柄、ストップロス、テイクプロフィットといった関連パラメータを渡すことで実行されます。他の種類の注文を実行する場合も、同様のアプローチを使用します。

      else if (sparam==obj_Btn_BUYSTOP.Name()){ //--- Check if the Buy Stop button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUYSTOP.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Ask + stopslevel*_Point; //--- Calculate the valid price for placing a buy stop order
         
         if (user_price < valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," < ",valid_price); //--- Log an error message
         }
         else if (user_price >= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the buy stop order
            double stopLoss = user_price-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.BuyStop(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the buy stop order
         }
      }
      else if (sparam==obj_Btn_SELLLIMIT.Name()){ //--- Check if the Sell Limit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELLLIMIT.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Bid + stopslevel*_Point; //--- Calculate the valid price for placing a sell limit order
         
         if (user_price < valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," < ",valid_price); //--- Log an error message
         }
         else if (user_price >= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the sell limit order
            double stopLoss = user_price+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.SellLimit(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the sell limit order
         }
      }
      else if (sparam==obj_Btn_BUYLIMIT.Name()){ //--- Check if the Buy Limit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUYLIMIT.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Ask - stopslevel*_Point; //--- Calculate the valid price for placing a buy limit order
         
         if (user_price > valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," > ",valid_price); //--- Log an error message
         }
         else if (user_price <= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the buy limit order
            double stopLoss = user_price-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.BuyLimit(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the buy limit order
         }
      }

ボタンをテストすると、次の出力データが得られます。

未決注文

うまくいきました。次は、ポジションのクローズと未決注文の削除に進みます。まず、すべての市場ポジションをクローズするロジックから始めましょう。

      else if (sparam==obj_Btn_CLOSE_ALL.Name()){ //--- Check if the Close All button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     obj_Trade.PositionClose(pos_ticket); //--- Close the position
                  }
               }
            }
         }
      }

ここでは、[Close All]ボタンを自動化するロジックを処理します。このボタンをクリックすると、すべてのアクティブなポジションが閉じられるように動作します。まず、ボタンがクリックされたかどうかを確認し、Print関数を使用して、デバッグ目的でクリックされたボタンの名前をログに記録します。次に、PositionsTotal関数を使用して口座内のすべてのポジションを反復処理するためのforループを開始します。この関数はポジションの総数を返します。ループはリストの最後の位置(「PositionsTotal() - 1」で表される)から始まり、逆方向に処理されます。各反復後に「i」をデクリメントすることで、すべてのポジションが適切に処理されるようにします。

各ループ内では、PositionGetTicket関数を使用して現在のポジションのチケット番号を取得します。このチケット番号は、各ポジションを一意に識別します。次に、そのチケットが有効かどうかを確認するために、チケット番号が0より大きいことをチェックします。有効な場合、PositionSelectByTicket関数を使用して、そのチケットに関連付けられたポジションを選択します。

ポジションが選択されると、PositionGetString関数をPOSITION_SYMBOLパラメータとともに使用し、現在の取引銘柄を表す_Symbolと比較します。これにより、ポジションが管理対象の銘柄と一致しているかどうかを確認します。一致している場合は、obj_TradeオブジェクトのPositionClose関数を呼び出し、チケット番号をパラメータとして渡してポジションをクローズします。このプロセスは、取引銘柄に一致するすべてのポジションがクローズされるまで繰り返されます。さらに、売りポジションのみをクローズする場合には、同じPositionClose関数を使用しつつ、以下のように追加の制御ロジックを実装します。

      else if (sparam==obj_Btn_CLOSE_ALL_SELL.Name()){ //--- Check if the Close All Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        obj_Trade.PositionClose(pos_ticket); //--- Close the sell position
                     }
                  }
               }
            }
         }
      }

これは、すべてのポジションをクローズするために使用した以前のアプローチと類似しています。そのため、ここでは追加セクションを強調表示し、[Close All Sell]ボタンがクリックされたときに売りポジションのみをクローズするための追加ロジックに焦点を当てます。すべてのポジションをループ処理した後、定数POSITION_TYPEを使用して、PositionGetInteger関数で各ポジションのポジションタイプを取得します。この値は、理解しやすくするために列挙型ENUM_POSITION_TYPEにキャストされます。このプロセスは型キャストと呼ばれるものです。ポジションタイプを取得した後、POSITION_TYPE_SELLプロパティと比較することで、現在のポジションが売りポジションであるかどうかを特定します。条件がtrue(つまり、ポジションが実際に売りである)と評価された場合、obj_TradeオブジェクトのPositionClose関数を呼び出し、該当する売りポジションをクローズします。同様のロジックを使用して買いポジションをクローズする際も、対応する条件(POSITION_TYPE_BUY)をチェックすることで実行できます。

      else if (sparam==obj_Btn_CLOSE_ALL_BUY.Name()){ //--- Check if the Close All Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        obj_Trade.PositionClose(pos_ticket); //--- Close the buy position
                     }
                  }
               }
            }
         }
      }

損失が発生しているすべての売りポジションをクローズする場合、現在のアプローチを拡張して、選択された各ポジションの利益データを取り込む必要があります。この利益データを利用して、ポジションが利益を出しているのか、それとも損失を出しているのかを比較して判断することができます。以下がそのロジックです。

      else if (sparam==obj_Btn_CLOSE_LOSS_SELL.Name()){ //--- Check if the Close Loss Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_LOSS_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss < 0){ //--- Check if the position is at a loss
                           obj_Trade.PositionClose(pos_ticket); //--- Close the losing sell position
                        }
                     }
                  }
               }
            }
         }
      }

ここでは、すべてのポジションをループし、現在のポジションが売りタイプであることを確認した後、各ポジションの利益/損失をチェックするロジックを導入します。PositionGetDouble関数は、定数POSITION_PROFITを使用して、ポジションが利益か損失かを判断し、選択したポジションの現在の利益/損失を取得します。次に、利益/損失の値がゼロ未満であるかどうかをチェックする条件文を追加します。ゼロ未満の場合、ポジションが損失であることを示します。この条件が満たされた場合、PositionClose関数を呼び出して、損失を出している売りポジションをクローズします。黄色で強調表示されているこの追加ロジックにより、現在損失が発生している売りポジションのみがクローズされることが保証されます。他のボタンにも同様のアプローチを使用します。

      else if (sparam==obj_Btn_CLOSE_LOSS_BUY.Name()){ //--- Check if the Close Loss Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_LOSS_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss < 0){ //--- Check if the position is at a loss
                           obj_Trade.PositionClose(pos_ticket); //--- Close the losing buy position
                        }
                     }
                  }
               }
            }
         }
      }
      
      else if (sparam==obj_Btn_CLOSE_PROFIT_SELL.Name()){ //--- Check if the Close Profit Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PROFIT_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss >= 0){ //--- Check if the position is profitable
                           obj_Trade.PositionClose(pos_ticket); //--- Close the profitable sell position
                        }
                     }
                  }
               }
            }
         }
      }

      else if (sparam==obj_Btn_CLOSE_PROFIT_BUY.Name()){ //--- Check if the Close Profit Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PROFIT_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss >= 0){ //--- Check if the position is profitable
                           obj_Trade.PositionClose(pos_ticket); //--- Close the profitable buy position
                        }
                     }
                  }
               }
            }
         }
      }      
      else if (sparam==obj_Btn_CLOSE_ALL_LOSS.Name()){ //--- Check if the Close All Loss button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_LOSS.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                     if (profit_loss < 0){ //--- Check if the position is at a loss
                        obj_Trade.PositionClose(pos_ticket); //--- Close the losing position
                     }
                  }
               }
            }
         }
      }            
      else if (sparam==obj_Btn_CLOSE_ALL_PROFIT.Name()){ //--- Check if the Close All Profit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_PROFIT.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                     if (profit_loss >= 0){ //--- Check if the position is profitable
                        obj_Trade.PositionClose(pos_ticket); //--- Close the profitable position
                     }
                  }
               }
            }
         }
      }            

ポジションのロジックが完了したら、未決注文のロジックに進むことができます。ここでは、未決注文をすべて削除します。これは次のロジックによって実現されます。

      else if (sparam==obj_Btn_CLOSE_PENDING.Name()){ //--- Check if the Close Pending button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PENDING.Name()); //--- Log the button click event
         
         for (int i = OrdersTotal() -1; i >= 0; i--){ //--- Loop through all pending orders
            ulong order_ticket = OrderGetTicket(i); //--- Get the ticket of the order
            if (order_ticket > 0){ //--- Check if the order ticket is valid
               if (OrderSelect(order_ticket)){ //--- Select the order by ticket
                  if (OrderGetString(ORDER_SYMBOL)==_Symbol){ //--- Check if the order matches the symbol
                     obj_Trade.OrderDelete(order_ticket); //--- Delete the pending order
                  }
               }
            }
         }
      }

ここでは、[Close Pending]ボタンがクリックされたときに未決注文を閉じる機能に焦点を当てます。まず、文字列パラメータが[Close Pending]ボタンの名前と一致するかどうかを確認します。これにより、ボタンがアクティブ化されたことがわかります。ボタンのクリックを確認した後、クリックされたボタンの名前を出力するPrint関数を使用して、デバッグの目的でアクションをログに記録します。次に、OrdersTotal関数を使用して注文の合計数を取得し、削除中に注文がスキップされないようにインデックスを逆順に減らして、すべての未決注文を反復処理するループに入ります。各注文について、OrderGetTicket関数を使用して注文チケット番号を取得します。次に、チケットがゼロより大きいかどうかを確認して、チケットが有効かどうかをチェックします。有効な場合は、OrderSelect関数を使用して注文を選択します。

注文が選択されると、ORDER_SYMBOL定数を使用してOrderGetString関数を呼び出し、_Symbolで指定された銘柄に対応しているかどうかを確認します。注文が一致する場合、OrderDelete関数を呼び出して、取得したチケットに関連する未決注文を削除します。このプロセス全体により、ボタンがアクティブ化されたときに、指定された取引銘柄にリンクされたすべての未決注文を効率的にクローズすることができ、ユーザーが注文を効果的に管理する意図が確保されます。その後、ChartRedraw関数を使用してチャートを更新し、変更がチャートに反映されることを確認します。

   ChartRedraw(0);

パネルを自動化するために必要なのはこれだけです。プログラムを実行してクロージャインターフェイスをテストすると、次の出力が得られます。

閉鎖セクション

チャートのクリックイベントを処理するためにシステムに組み込まれたOnChartEventイベントハンドラのロジックは次のとおりです。

//+------------------------------------------------------------------+
//| Handling chart events                                            |
//+------------------------------------------------------------------+
void OnChartEvent(
   const int       id,       // Event ID indicating the type of event (e.g., mouse click, timer, etc.)
   const long&     lparam,   // Long type parameter associated with the event, usually containing data like mouse coordinates or object IDs
   const double&   dparam,   // Double type parameter associated with the event, used for floating-point values related to the event
   const string&   sparam    // String type parameter associated with the event, typically the name of the object that triggered the event
){
   // Print the 4 function parameters    
   //Print("ID = ",id,", LPARAM = ",lparam,", DPARAM = ",dparam,", SPARAM = ",sparam);

   // Check if the event is a click on a chart object
   if (id == CHARTEVENT_OBJECT_CLICK){
      //Print("ID = ",id,", LM = ",lparam,", DM = ",dparam,", SPARAM = ",sparam);
      // Check if the clicked object is the Trade button
      if (sparam==obj_Btn_TRADE.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_TRADE.Name());

         // Reset the pressed states of all buttons to ensure only one button appears pressed at a time
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Change the background color of the Trade button to yellow to indicate it is active
         obj_Btn_TRADE.ColorBackground(clrYellow);
         // Set the background color of the Close and Info buttons to silver to indicate they are inactive
         obj_Btn_CLOSE.ColorBackground(clrSilver);
         obj_Btn_INFO.ColorBackground(clrSilver);

         // Set the border color of the Trade button to yellow to match its background
         obj_Btn_TRADE.ColorBorder(clrYellow);
         // Set the border color of the Close and Info buttons to silver to indicate they are inactive
         obj_Btn_CLOSE.ColorBorder(clrSilver);
         obj_Btn_INFO.ColorBorder(clrSilver);

         // Call a function to destroy the Close section if it exists
         destroySection_Close();
         // Call a function to destroy the Information section if it exists
         destroySection_Information();

         // Create the Trade section, bringing it to the forefront
         createSection_Trade();
      }
      // Check if the clicked object is the Close button
      else if (sparam==obj_Btn_CLOSE.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_CLOSE.Name());

         // Reset the pressed states of all buttons
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Set the background color of the Trade button to silver, indicating it's inactive
         obj_Btn_TRADE.ColorBackground(clrSilver);
         // Change the background color of the Close button to yellow to indicate it is active
         obj_Btn_CLOSE.ColorBackground(clrYellow);
         obj_Btn_INFO.ColorBackground(clrSilver);

         // Set the border color of the Trade button to silver
         obj_Btn_TRADE.ColorBorder(clrSilver);
         // Set the border color of the Close button to yellow
         obj_Btn_CLOSE.ColorBorder(clrYellow);
         obj_Btn_INFO.ColorBorder(clrSilver);

         // Call a function to destroy the Trade section if it exists
         destroySection_Trade();
         // Call a function to destroy the Information section if it exists
         destroySection_Information();

         // Create the Close section, bringing it to the forefront
         createSection_Close();
      }
      // Check if the clicked object is the Information button
      else if (sparam==obj_Btn_INFO.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_INFO.Name());

         // Reset the pressed states of all buttons
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Set the background color of the Trade and Close buttons to silver, indicating they are inactive
         obj_Btn_TRADE.ColorBackground(clrSilver);
         obj_Btn_CLOSE.ColorBackground(clrSilver);
         // Change the background color of the Info button to yellow to indicate it is active
         obj_Btn_INFO.ColorBackground(clrYellow);

         // Set the border color of the Trade and Close buttons to silver
         obj_Btn_TRADE.ColorBorder(clrSilver);
         obj_Btn_CLOSE.ColorBorder(clrSilver);
         // Set the border color of the Info button to yellow
         obj_Btn_INFO.ColorBorder(clrYellow);

         // Call a function to destroy the Trade section if it exists
         destroySection_Trade();
         // Call a function to destroy the Close section if it exists
         destroySection_Close();

         // Create the Information section, bringing it to the forefront
         createSection_Information();
      }
      // Check if the clicked object is the exit button (X button)
      else if (sparam==obj_Btn_X.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_X.Name());

         // Call functions to destroy all sections, effectively closing the entire panel
         destroySection_Trade();
         destroySection_Close();
         destroySection_Information();

         // Call a function to destroy the main panel itself
         destroySection_Main_Panel();
      }
      else if (sparam==obj_Btn_SELL.Name()){ //--- Check if the Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELL.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
         double entry_price = Bid; //--- Set the entry price for selling to the current bid price
         double stopLoss = Ask+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
         double takeprofit = Ask-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
         
         Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
         obj_Trade.Sell(lots,_Symbol,entry_price,stopLoss,takeprofit); //--- Execute the sell order
      }
      else if (sparam==obj_Btn_BUY.Name()){ //--- Check if the Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUY.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
         double entry_price = Ask; //--- Set the entry price for buying to the current ask price
         double stopLoss = Bid-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
         double takeprofit = Bid+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
         
         Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
         obj_Trade.Buy(lots,_Symbol,entry_price,stopLoss,takeprofit); //--- Execute the buy order
      }
      else if (sparam==obj_Btn_SELLSTOP.Name()){ //--- Check if the Sell Stop button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELLSTOP.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Bid - stopslevel*_Point; //--- Calculate the valid price for placing a sell stop order
         
         if (user_price > valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," > ",valid_price); //--- Log an error message
         }
         else if (user_price <= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the sell stop order
            double stopLoss = user_price+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.SellStop(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the sell stop order
         }
      }
      else if (sparam==obj_Btn_BUYSTOP.Name()){ //--- Check if the Buy Stop button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUYSTOP.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Ask + stopslevel*_Point; //--- Calculate the valid price for placing a buy stop order
         
         if (user_price < valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," < ",valid_price); //--- Log an error message
         }
         else if (user_price >= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the buy stop order
            double stopLoss = user_price-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.BuyStop(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the buy stop order
         }
      }
      else if (sparam==obj_Btn_SELLLIMIT.Name()){ //--- Check if the Sell Limit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELLLIMIT.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Bid + stopslevel*_Point; //--- Calculate the valid price for placing a sell limit order
         
         if (user_price < valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," < ",valid_price); //--- Log an error message
         }
         else if (user_price >= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the sell limit order
            double stopLoss = user_price+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.SellLimit(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the sell limit order
         }
      }
      else if (sparam==obj_Btn_BUYLIMIT.Name()){ //--- Check if the Buy Limit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUYLIMIT.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Ask - stopslevel*_Point; //--- Calculate the valid price for placing a buy limit order
         
         if (user_price > valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," > ",valid_price); //--- Log an error message
         }
         else if (user_price <= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the buy limit order
            double stopLoss = user_price-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.BuyLimit(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the buy limit order
         }
      }

      
      else if (sparam==obj_Btn_CLOSE_ALL.Name()){ //--- Check if the Close All button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     obj_Trade.PositionClose(pos_ticket); //--- Close the position
                  }
               }
            }
         }
      }
      else if (sparam==obj_Btn_CLOSE_ALL_SELL.Name()){ //--- Check if the Close All Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        obj_Trade.PositionClose(pos_ticket); //--- Close the sell position
                     }
                  }
               }
            }
         }
      }
      else if (sparam==obj_Btn_CLOSE_ALL_BUY.Name()){ //--- Check if the Close All Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        obj_Trade.PositionClose(pos_ticket); //--- Close the buy position
                     }
                  }
               }
            }
         }
      }
      else if (sparam==obj_Btn_CLOSE_LOSS_SELL.Name()){ //--- Check if the Close Loss Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_LOSS_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss < 0){ //--- Check if the position is at a loss
                           obj_Trade.PositionClose(pos_ticket); //--- Close the losing sell position
                        }
                     }
                  }
               }
            }
         }
      }
      else if (sparam==obj_Btn_CLOSE_LOSS_BUY.Name()){ //--- Check if the Close Loss Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_LOSS_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss < 0){ //--- Check if the position is at a loss
                           obj_Trade.PositionClose(pos_ticket); //--- Close the losing buy position
                        }
                     }
                  }
               }
            }
         }
      }
      
      else if (sparam==obj_Btn_CLOSE_PROFIT_SELL.Name()){ //--- Check if the Close Profit Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PROFIT_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss >= 0){ //--- Check if the position is profitable
                           obj_Trade.PositionClose(pos_ticket); //--- Close the profitable sell position
                        }
                     }
                  }
               }
            }
         }
      }

      else if (sparam==obj_Btn_CLOSE_PROFIT_BUY.Name()){ //--- Check if the Close Profit Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PROFIT_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss >= 0){ //--- Check if the position is profitable
                           obj_Trade.PositionClose(pos_ticket); //--- Close the profitable buy position
                        }
                     }
                  }
               }
            }
         }
      }      
      else if (sparam==obj_Btn_CLOSE_ALL_LOSS.Name()){ //--- Check if the Close All Loss button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_LOSS.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                     if (profit_loss < 0){ //--- Check if the position is at a loss
                        obj_Trade.PositionClose(pos_ticket); //--- Close the losing position
                     }
                  }
               }
            }
         }
      }            
      else if (sparam==obj_Btn_CLOSE_ALL_PROFIT.Name()){ //--- Check if the Close All Profit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_PROFIT.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                     if (profit_loss >= 0){ //--- Check if the position is profitable
                        obj_Trade.PositionClose(pos_ticket); //--- Close the profitable position
                     }
                  }
               }
            }
         }
      }            
      else if (sparam==obj_Btn_CLOSE_PENDING.Name()){ //--- Check if the Close Pending button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PENDING.Name()); //--- Log the button click event
         
         for (int i = OrdersTotal() -1; i >= 0; i--){ //--- Loop through all pending orders
            ulong order_ticket = OrderGetTicket(i); //--- Get the ticket of the order
            if (order_ticket > 0){ //--- Check if the order ticket is valid
               if (OrderSelect(order_ticket)){ //--- Select the order by ticket
                  if (OrderGetString(ORDER_SYMBOL)==_Symbol){ //--- Check if the order matches the symbol
                     obj_Trade.OrderDelete(order_ticket); //--- Delete the pending order
                  }
               }
            }
         }
      }

      
   }
   
   
   ChartRedraw(0);
   
}

一言で言えば、これが私たちが成し遂げたことです。

フルパネルイラスト

素晴らしかったです。パネルを完全にインタラクティブかつ応答性の高いものにすることに成功しました。ボタンのクリック、ライブデータの更新、アクティブ状態への応答性などの機能が追加され、取引インターフェースの全体的なユーザーエクスペリエンスと機能性が向上しました。


結論

結論として、MetaQuotes Language 5 GUIパネルに実装した機能強化により、インタラクティブ性と機能性が大幅に向上し、より魅力的なユーザーエクスペリエンスが実現されました。ライブデータ更新と応答性の高いボタンクリックの追加により、パネルはシームレスかつ直感的に操作できるようになりました。これらの機能は、売買注文の実行を容易にするだけでなく、リアルタイムの取引口座情報にすぐにアクセスできるようになり、市場の変動に応じて迅速かつ情報に基づいた取引決定を下すことができるようにします。

さらに、ポジション管理や口座情報の表示など、さまざまなコンポーネントの自動化により、取引プロセスの利便性と効率性がさらに高まります。ユーザーがクリックするだけでポジションや注文をクローズできるようにし、カスタマイズ可能なオプションを提供することで、このGUIパネルは現代のトレーダーにとって強力なツールとなります。この変革により、よりクリーンで整理されたワークスペースが実現され、集中力と生産性が向上します。この記事がMQL5 GUIパネルの強化に関する貴重な洞察を提供し、説明が明確で有益であったことを願っています。取引をお楽しみください。

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

添付されたファイル |
Connexusのリクエスト(第6回):HTTPリクエストとレスポンスの作成 Connexusのリクエスト(第6回):HTTPリクエストとレスポンスの作成
Connexusライブラリ連載第6回目では、HTTPリクエストの構成要素全体に焦点を当て、リクエストを構成する各コンポーネントを取り上げます。そして、リクエスト全体を表現するクラスを作成し、これまでに作成したクラスを統合します。
MQL5で取引管理者パネルを作成する(第5回):2要素認証(2FA) MQL5で取引管理者パネルを作成する(第5回):2要素認証(2FA)
本日は、現在開発中の取引管理パネルのセキュリティ強化について説明します。Telegram APIを統合し、2要素認証(2FA)を実現する新しいセキュリティ戦略にMQL5を実装する方法を探ります。このディスカッションでは、MQL5を活用してセキュリティ対策を強化する方法について貴重な洞察を得ることができます。さらに、MathRand関数の機能に焦点を当て、セキュリティフレームワーク内でどのように効果的に活用できるかを検討します。さらに詳しく知りたい方は、読み続けてください。
ニュース取引が簡単に(第5回):取引の実施(II) ニュース取引が簡単に(第5回):取引の実施(II)
この記事では、取引管理クラスを拡張し、ニュースイベントを取引するための買い逆指値注文(買いストップ注文)と売り逆指値注文(売りストップ注文)を追加します。また、オーバーナイト取引を防ぐために、これらの注文に有効期限の制約を実装します。さらに、逆指値注文(ストップ注文)を使用する際に発生しうるスリッページ、特にニュースイベント中に発生する可能性のあるスリッページを防止または最小限に抑えるために、スリッページ関数をエキスパートアドバイザー(EA)に組み込みます。
Candlestick Trend Constraintモデルの構築(第9回):マルチ戦略エキスパートアドバイザー(II) Candlestick Trend Constraintモデルの構築(第9回):マルチ戦略エキスパートアドバイザー(II)
エキスパートアドバイザー(EA)に統合できる戦略の数は、事実上無限と言えます。しかし、戦略を追加するたびにアルゴリズムの複雑さが増していきます。複数の戦略を組み込むことで、EAは多様な市場環境により柔軟に適応し、収益性を向上させる可能性が高まります。本日は、Trend Constraint EAの機能をさらに強化するための取り組みとして、リチャード・ドンチャンが開発した著名な戦略のひとつを対象に、MQL5を活用する方法をご紹介します。
OSZAR »