English Deutsch
preview
MQL5で取引管理者パネルを作成する(第9回):コード編成(V):AnalyticsPanelクラス

MQL5で取引管理者パネルを作成する(第9回):コード編成(V):AnalyticsPanelクラス

MetaTrader 5 |
84 1
Clemence Benjamin
Clemence Benjamin

内容


はじめに

前のセクションでは、分析パネルのコンセプトを紹介しましたが、その時点では主に、勝敗比率を表示する円グラフのような静的なインターフェイス要素で構成されていました。しかし、これらのコンポーネントはリアルタイムのデータ更新がなかったため、動的な取引環境では使い勝手が限られていました。本稿では、インターフェイスの設計と機能性の両方を大きく進化させ、リアルタイムの取引データの取得と表示に焦点を当てて大きな進歩を目指します。

私たちの開発手法は、MQL5標準ライブラリ、特に\Include\Controls\ディレクトリにあるクラスを活用します。既存のクラスであるCLabel、CEdit、CDialogなどを拡張・カスタマイズし、応答性が高くデータ駆動型のUIコンポーネントを作成します。これらは、総合的な口座管理や戦略管理を提供し、Telegramを介したリモート操作やリアルタイム通知のためのコミュニケーションインターフェイスも備えた、モジュラーかつ多機能な取引管理パネルの中核を成します。

本稿の主目的は高度なパネルの開発ですが、ここで紹介する技術、特にリアルタイムデータ取得と動的UI更新の手法は、管理インターフェイスに限らず、エキスパートアドバイザー(EA)やカスタムインジケーター、インタラクティブな学習ツールなど、さまざまなMQL5プロジェクトに応用できます。


AnalyticsPanelクラスの概要

大規模なMQL5プログラムに適したモジュラー開発アプローチの一環として、またコードの再利用性と保守性を高めるために、専用のAnalyticsPanelクラスのヘッダーファイルを作成しています。このクラスは、アナリティクスパネルの視覚的なレイアウトと、リアルタイムの市場データ取得および表示の両方をカプセル化することを目的としています。

パネルは標準的な口座指標の表示に加えて、私が「コンフルエンス戦略」と名付けたカスタム戦略に基づく様々なテクニカル指標の値も表示します。この戦略は、複数のインジケーターからのシグナルを比較して統一的な取引シグナルを生成する「コンフルエンス(収束)」の原理に基づいています。もし指標間で合意が得られない場合、パネルには「No Consensus」のメッセージが表示され、誤検出や弱いシグナルを避ける設計となっています。

AnalyticsPanelクラスには、パネルレイアウトの初期化と更新、ラベル値のリアルタイム更新、そして戦略ロジックに基づく視覚的なシグナルフィードバックの管理といったメソッドが含まれます。以下にパネルの視覚デザインレイアウトを示し、その後の解説で実装の詳細について順を追って説明していきます。

AnalyticsPanelデザイン

AnalyticsPanelの機能

開発の継続的な取り組みの一環として、クラスを開発した後、それをメインのEAであるNew_Admin_Panelプログラムに統合します。この統合はシステムのモジュラーアーキテクチャを強化するとともに、再利用可能で独立したコンポーネントを構築し、それらが大規模なアプリケーションフレームワーク内でシームレスに連携できることの利点を示します。以下の画像は、すべてのインジケーター値が一致し、強い合意を形成した場合にのみ売買シグナルが生成される、完成した製品の最終的なパフォーマンスを示します。

AnalyticsPanelの動作

AnalyticsPanelの動作

AnalyticsPanelをNew_Admin_Panelに統合することで、いくつかの実用的なメリットが得られます。

  • 統合されたモニタリング:勝敗比率やエクイティの変動、取引サマリーなどのリアルタイム分析を、別ツールを使わずにメインインターフェイスから直接確認できます。
  • 向上したユーザー体験:分析機能と戦略実行、Telegramメッセージ送信などのコア機能を組み合わせることで、より統一され直感的なワークフローを実現します。
  • コードの拡張性と保守性の向上:AnalyticsPanelのようなインターフェイスコンポーネントをクラスとして分離することで、コードの読みやすさが増し、テストが容易になり、将来のアップグレードもスムーズにおこなえます。
  • プロジェクト間での再利用性:モジュラー設計により、AnalyticsPanelや他のパネルは他のEAや取引ユーティリティに再利用または適応可能です。
  • 取引シグナル:現バージョンは、有用なコンフルエンスシグナルを生成し、取引判断に役立ちます。



MQL5の実装

分析パネルの設計にあたっては、オブジェクト指向の原則に従い、主要なコンポーネントを異なるアクセスレベルに分けることで、コードの明確さ、安全性、保守性を向上させています。クラスは、パネルとのやり取りに使うpublic要素と、内部の動作やデータ管理を扱うprivate要素に分けて構成されています。このカプセル化の手法により、パネルの設計図が明確かつ整理された形で示され、将来の開発でも各セクションの役割が把握しやすくなります。 

CAnalyticsPanelクラスを定義する前に、以下の2つの重要なヘッダーファイルをインクルードします。

Dialog.mqh:ダイアログ関連クラスへのアクセスを提供するヘッダーで、カスタムパネル作成の基底クラスとなるCAppDialogを含みます。

Label.mqh:パネル上のすべてのテキストラベルの作成と管理に使用されるCLabelクラスの定義が含まれています。

これらのインクルードは、カスタムパネルがMQL5の標準UIコントロール構造にアクセスできるようにするために必要です。これがなければ、パネルクラスはCAppDialogを継承したり、ラベルコントロールを作成したりできません。

また、パネルの視覚的構造をレスポンシブかつ整然と保つために、スペーシングやレイアウト用のマクロ(AP_GAP、AP_DEFAULT_LABEL_HEIGHT)も定義する必要があります。
#include <Controls\Dialog.mqh>
#include <Controls\Label.mqh>

// Use unique macro names for layout in this class:
#define AP_DEFAULT_LABEL_HEIGHT 20
#define AP_GAP                10

以下の概要は、クラスの主な構成要素を5つの主要領域に分類したものです。

1. クラス構造と目的

CAnalyticsPanelクラスは、MetaTrader 5のチャート上にリアルタイムで取引および市場状況の情報を表示する、非常に視覚的かつ情報豊富なダッシュボードとして機能します。MQL5標準ライブラリのCAppDialogクラスを継承しており、オブジェクト指向プログラミングを活用して、データを整理されたセクションに分けて表示します。このパネルには、口座情報、保有中のポジション、市場の価格、インジケーター、そして統合されたシグナルの概要が含まれており、トレーダーがすべての重要情報を一つのコンパクトなウィンドウで監視できるよう設計されています。 

//+------------------------------------------------------------------+
//| Analytics Panel Class                                            |
//+------------------------------------------------------------------+
class CAnalyticsPanel : public CAppDialog
{
protected:
   // Helper: Create a text label.
   // The label's text color is set as provided and its background is forced to black.
   bool CreateLabelEx(CLabel &label, int x, int y, int width, int height, string label_name, string text, color clr)
   {
      // Construct a unique control name by combining the dialog name and label_name.
      string unique_name = m_name + "_" + label_name;
      if(!label.Create(m_chart_id, unique_name, m_subwin, x, y, x+width, y+height))
      {
         Print("Failed to create label: ", unique_name, " Err=", GetLastError());
         return false;
      }
      if(!Add(label))
      {
         Print("Failed to add label: ", unique_name, " Err=", GetLastError());
         return false;
      }
      if(!label.Text(text))
      {
         Print("Failed to set text for label: ", unique_name);
         return false;
      }
      label.Color(clr);          // Set text color
      label.Color(clrBlack);   // Force background to black
      return true;
   }

   // Account and Trade Data Labels:
   CLabel m_lblBalance;
   CLabel m_lblEquity;
   CLabel m_lblMargin;
   CLabel m_lblOpenTrades;
   // PnL is split into two labels:
   CLabel m_lblPnLName;
   CLabel m_lblPnLValue;

   // Market Data Labels (split Bid/Ask):
   CLabel m_lblBidName;
   CLabel m_lblBidValue;
   CLabel m_lblAskName;
   CLabel m_lblAskValue;
   CLabel m_lblSpread;

   // Indicator Section Separator:
   CLabel m_lblIndicatorSeparator;
   // New header for indicator section:
   CLabel m_lblIndicatorHeader;

   // Indicator Labels (static name and dynamic value labels):
   CLabel m_lblRSIName;
   CLabel m_lblRSIValue;
   CLabel m_lblStochName;
   CLabel m_lblStochK;  // %K value
   CLabel m_lblStochD;  // %D value
   CLabel m_lblCCIName;
   CLabel m_lblCCIValue;
   CLabel m_lblWilliamsName;
   CLabel m_lblWilliamsValue;

   // New Summary Labels (for consensus across indicators):
   CLabel m_lblSignalHeader;    // Header label for summary column ("SIGNAL")
   CLabel m_lblExpectation;     // Text summary (e.g., "Buy" or "Sell")
   CLabel m_lblConfirmation;    // Confirmation symbol (e.g., up or down arrow)

   // Previous values for dynamic color changes (market data):
   double m_prevBid;
   double m_prevAsk;

public:
   CAnalyticsPanel();
   virtual ~CAnalyticsPanel();

   // Create the panel and initialize UI controls.
   virtual bool CreatePanel(const long chart, const string name, const int subwin,
                            int x1, int y1, int x2, int y2);

   // Update the panel with the latest account, trade, market and indicator data.
   void UpdatePanel();
   void Toggle();

private:
   void CreateControls();
   void UpdateAccountInfo();
   void UpdateTradeStats();
   void UpdateMarketData();
   void UpdateIndicators();
};

以下に変数の詳細な概要と説明を示します。

メンバー変数

口座と取引データのUIラベル:

口座ラベル:

  • m_lblBalance、m_lblEquity、m_lblMarginはそれぞれ口座残高、資産、証拠金を表示します。
取引統計:
  • m_lblOpenTradesは現在未決の取引の数を表示します。
PnL(損益)情報は2つのラベルに分割されます。
  1. m_lblPnLName:「PnL:」というテキストを単に表示する静的ラベル(黒のまま)
  2. m_lblPnLValue:数値のPnL値を表示し、色が変化する動的なラベル(利益の場合は緑、損失の場合は赤)

市場データラベル:

これらのラベルには、Bid/Ask値とスプレッド情報が表示されます。

  • Bid値のm_lblBidNameとm_lblBidValue
  • Ask値m_lblAskNameとm_lblAskValue
  • m_lblSpreadは現在のスプレッドをpips単位で表示します。
  • Bid値とAsk値の色は、市場の変化を反映して動的に更新されます(たとえば、値が増加すると青になり、値が減少すると赤になります)。

インジケーターセクション:

  • セパレーター(m_lblIndicatorSeparator)は、インジケーターセクションをパネルの残りの部分から視覚的に区別します。
  • ヘッダーラベル(m_lblIndicatorHeader)には、セクションを明確に識別する「INDICATORS」が表示されます。

このセクションにはいくつかのインジケーターが含まれています。

  • RSI:2つのラベル、m_lblRSINameとm_lblRSIValueは、名前("RSI:")と現在値を表示します。
  • ストキャスティクス:このインジケーターは次の2つの部分に分かれています。
          1. m_lblStochName:静的部分("Stoch:")
          2. m_lblStochKとm_lblStochD:%Kと%Dの動的な値を表示
  • CCI:m_lblCCINameとm_lblCCIValueは商品チャネルインデックスを示します。
  • Williams %R:m_lblWilliamsNameとm_lblWilliamsValueはWilliams %R値を表示します。

要約(コンセンサス)セクション:

  • ヘッダーラベル(m_lblSignalHeader)には、要約列の上に「SIGNAL」という単語が表示されます。
  • 2つのラベル、m_lblExpectationとm_lblConfirmationは、すべてのインジケーターから得られたコンセンサスシグナルを表示するために使用されます。
    1. m_lblExpectationは、「Buy」「Sell」「No Consensus」といったテキストによるシグナルの要約を表示します。
    2. m_lblConfirmationは、視覚的な確認を示します。たとえば、「↑」(買い)、「↓」(売り)、「-」(コンセンサスなし)といった記号が使用されます。

市場データの内部状態

2つの変数、m_prevBidとm_prevAskは、直前のBidおよびAsk価格を保持します。これにより、現在の価格が上昇しているのか下降しているのかを判断し、それに応じた色分け表示が可能になります。

2. ラベル作成のためのヘルパーメソッド

クラス内の主要なユーティリティ関数のひとつがCreateLabelExです。このメソッドは、パネル上にテキストラベルを作成・カスタマイズするための再利用可能な関数であり、以下の手順をすべてカプセル化しています。ラベルのインスタンス化、ユニークな名前の割り当て、サイズと位置の設定、テキストや色の適用、そしてパネルのコントロールリストへの追加を一括しておこないます。  このプロセスのいずれかの段階で失敗した場合は、適切なエラーメッセージがログに記録され、デバッグに役立てられるようになっています。

bool CAnalyticsPanel::CreateLabelEx(CLabel &label, int x, int y, int width, int height,
                                    string label_name, string text, color clr)
{
   string unique_name = m_name + "_" + label_name;
   if(!label.Create(m_chart_id, unique_name, m_subwin, x, y, x+width, y+height))
   {
      Print("Failed to create label: ", unique_name, " Err=", GetLastError());
      return false;
   }
   if(!Add(label))
   {
      Print("Failed to add label: ", unique_name, " Err=", GetLastError());
      return false;
   }
   if(!label.Text(text))
   {
      Print("Failed to set text for label: ", unique_name);
      return false;
   }
   label.Color(clr);          // Set text color
   label.BackColor(clrBlack); // Force background to black
   return true;
}
    

3. ユーザーインターフェイスレイアウトの整理

CreateControlsメソッドは、パネル上のすべてのユーザーインターフェイス要素を整理・配置する役割を担います。このメソッドでは、口座情報(残高、純資産、証拠金)、取引統計(保有ポジション数、損益)、市場データ(Bid、Ask、スプレッド)、インジケーターセクションの順に各セクションを配置します。インジケーターセクションでは、「INDICATORS」や「SIGNAL」などのヘッダーを作成し、情報のグループを明確にラベリングします。コード内では、ラベルの高さや間隔を示す定数をあらかじめ定義しており、すべての要素が均等に配置され、視覚的に整理されたデザインになるよう配慮されています。

//+------------------------------------------------------------------+
//| CreateControls: Instantiate and add UI controls                  |
//+------------------------------------------------------------------+
void CAnalyticsPanel::CreateControls()
{
   int curX = AP_GAP;
   int curY = AP_GAP;
   int labelWidth = 150;

   // Account Information Labels:
   CreateLabelEx(m_lblBalance, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Balance", "Balance: $0.00", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;
   CreateLabelEx(m_lblEquity, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Equity", "Equity: $0.00", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;
   CreateLabelEx(m_lblMargin, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Margin", "Margin: 0", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;

   // Trade Statistics Labels:
   CreateLabelEx(m_lblOpenTrades, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "OpenTrades", "Open Trades: 0", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;
   // PnL labels:
   CreateLabelEx(m_lblPnLName, curX, curY, 50, AP_DEFAULT_LABEL_HEIGHT, "PnLName", "PnL:", clrBlack);
   CreateLabelEx(m_lblPnLValue, curX+50, curY, labelWidth-50, AP_DEFAULT_LABEL_HEIGHT, "PnLValue", "$0.00", clrLime);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;

   // Market Data Labels:
   CreateLabelEx(m_lblBidName, curX, curY, 40, AP_DEFAULT_LABEL_HEIGHT, "BidName", "Bid:", clrDodgerBlue);
   CreateLabelEx(m_lblBidValue, curX+40, curY, 100, AP_DEFAULT_LABEL_HEIGHT, "BidValue", "0.00000", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;
   CreateLabelEx(m_lblAskName, curX, curY, 40, AP_DEFAULT_LABEL_HEIGHT, "AskName", "Ask:", clrDodgerBlue);
   CreateLabelEx(m_lblAskValue, curX+40, curY, 100, AP_DEFAULT_LABEL_HEIGHT, "AskValue", "0.00000", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;
   CreateLabelEx(m_lblSpread, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Spread", "Spread: 0.0 pips", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;

   // Indicator Section Separator:
   CreateLabelEx(m_lblIndicatorSeparator, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Separator", "------------------------------------------", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;

   // Indicator Section Header:
   CreateLabelEx(m_lblIndicatorHeader, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "IndicatorHeader", "INDICATORS", clrDodgerBlue);
   // Summary Column Header for Signal:
   CreateLabelEx(m_lblSignalHeader, curX+250, curY , 100, AP_DEFAULT_LABEL_HEIGHT, "SignalHeader", "SIGNAL", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;

   // Indicator Labels:
   // RSI:
   CreateLabelEx(m_lblRSIName, curX, curY, 50, AP_DEFAULT_LABEL_HEIGHT, "RSIName", "RSI:", clrDodgerBlue);
   CreateLabelEx(m_lblRSIValue, curX+50, curY, 90, AP_DEFAULT_LABEL_HEIGHT, "RSIValue", "N/A", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;
   // Stochastic:
   CreateLabelEx(m_lblStochName, curX, curY, 60, AP_DEFAULT_LABEL_HEIGHT, "StochName", "Stoch:", clrDodgerBlue);
   CreateLabelEx(m_lblStochK, curX+60, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "StochK", "K: N/A", clrDodgerBlue);
   CreateLabelEx(m_lblStochD, curX+150, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "StochD", "D: N/A", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;
   // CCI:
   CreateLabelEx(m_lblCCIName, curX, curY, 50, AP_DEFAULT_LABEL_HEIGHT, "CCIName", "CCI:", clrDodgerBlue);
   CreateLabelEx(m_lblCCIValue, curX+50, curY, 90, AP_DEFAULT_LABEL_HEIGHT, "CCIValue", "N/A", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;
   // Williams %R:
   CreateLabelEx(m_lblWilliamsName, curX, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "WilliamsName", "Williams:", clrDodgerBlue);
   CreateLabelEx(m_lblWilliamsValue, curX+70, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "WilliamsValue", "N/A", clrDodgerBlue);
   curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP;

   // Summary Column: Expectation text and Confirmation symbol.
   CreateLabelEx(m_lblExpectation, curX+250, curY - (AP_DEFAULT_LABEL_HEIGHT+AP_GAP)*4, 100, AP_DEFAULT_LABEL_HEIGHT, "Expectation", "Expect: N/A", clrDodgerBlue);
   CreateLabelEx(m_lblConfirmation, curX+300, curY - (AP_DEFAULT_LABEL_HEIGHT+AP_GAP)*4, 50, AP_DEFAULT_LABEL_HEIGHT, "Confirmation", "N/A", clrDodgerBlue);

   ChartRedraw(m_chart_id);
}

4. 口座と市場情報のリアルタイムデータ更新

MQL5の組み込み関数を使用して、パネルは口座情報(残高、純資産、証拠金の生データ)や、市場データ(Bid、Ask価格、スプレッド)を取得します。UpdateAccountInfoメソッドとUpdateTradeStatsメソッドは、AccountInfoDoubleなどの関数を使用して口座に直接クエリを行います。一方、UpdateMarketDataはSymbolInfoTickを使ってリアルタイムのティックデータを取得し、現在の値と直前の値を比較して、BidおよびAskのラベルに対して変化に応じた色分けをおこなうようにします。これにより、トレーダーは口座および市場の最新状況をリアルタイムで把握できるようになります。

//+------------------------------------------------------------------+
//| UpdateAccountInfo: Refresh account-related data                  |
//+------------------------------------------------------------------+
void CAnalyticsPanel::UpdateAccountInfo()
{
   double balance = AccountInfoDouble(ACCOUNT_BALANCE);
   double equity  = AccountInfoDouble(ACCOUNT_EQUITY);
   double margin  = AccountInfoDouble(ACCOUNT_MARGIN);

   m_lblBalance.Text("Balance: $" + DoubleToString(balance, 2));
   m_lblEquity.Text("Equity: $" + DoubleToString(equity, 2));
   // Directly display the raw margin value.
   m_lblMargin.Text("Margin: " + DoubleToString(margin, 2));
}

//+------------------------------------------------------------------+
//| UpdateTradeStats: Refresh trade statistics                       |
//+------------------------------------------------------------------+
void CAnalyticsPanel::UpdateTradeStats()
{
   int total = PositionsTotal();
   double pnl = 0;
   for(int i = 0; i < total; i++)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket != 0)
         pnl += PositionGetDouble(POSITION_PROFIT);
   }
   // Update PnL labels:
   m_lblPnLName.Text("PnL:");  // static remains black
   m_lblPnLValue.Text("$" + DoubleToString(pnl, 2));
   if(pnl < 0)
      m_lblPnLValue.Color(clrRed);
   else
      m_lblPnLValue.Color(clrLime);

   m_lblOpenTrades.Text("Open Trades: " + IntegerToString(total));
}

//+------------------------------------------------------------------+
//| UpdateMarketData: Refresh market data display                    |
//+------------------------------------------------------------------+
void CAnalyticsPanel::UpdateMarketData()
{
   MqlTick last_tick;
   if(SymbolInfoTick(Symbol(), last_tick))
   {
      // Update Bid value and color based on change:
      color bidColor = clrBlue;
      if(m_prevBid != 0)
         bidColor = (last_tick.bid >= m_prevBid) ? clrBlue : clrRed;
      m_lblBidValue.Color(bidColor);
      m_lblBidValue.Text(DoubleToString(last_tick.bid, 5));

      // Update Ask value and color based on change:
      color askColor = clrBlue;
      if(m_prevAsk != 0)
         askColor = (last_tick.ask >= m_prevAsk) ? clrBlue : clrRed;
      m_lblAskValue.Color(askColor);
      m_lblAskValue.Text(DoubleToString(last_tick.ask, 5));

      m_prevBid = last_tick.bid;
      m_prevAsk = last_tick.ask;

      // Update Spread:
      double spread_pips = (last_tick.ask - last_tick.bid) * 1e4;
      m_lblSpread.Text("Spread: " + DoubleToString(spread_pips, 1) + " pips");
   }
}

5. テクニカル指標の計算とコンセンサスシグナルの生成

UpdateIndicatorsメソッドは、RSI、ストキャスティクス、CCI、Williams %Rといった複数のテクニカル指標の値を取得し、それぞれの最新データを読み取ることで処理をおこないます。各インジケーターの値は、あらかじめ定義されたしきい値と比較され、買いまたは売りのシグナルであるかを判定します。たとえば、RSIが30未満であれば「買い」、70を超えていれば「売り」と解釈されます。その後、パネルはこれら個別のシグナルを統合し、すべてのインジケーターが一致していれば、要約セクションに「Buy」または「Sell」と明確に表示され、対応する矢印記号(↑または↓)が表示されます。これにより、トレーダーは現在の市場状況を直感的かつ迅速に把握できる視覚的な手がかりを得ることができます。

//+------------------------------------------------------------------+
//| UpdateIndicators: Calculate and display various indicators       |
//+------------------------------------------------------------------+
void CAnalyticsPanel::UpdateIndicators()
{
   // Local suggestion variables (1 = Buy, -1 = Sell, 0 = Neutral)
   int suggestionRSI = 0, suggestionStoch = 0, suggestionCCI = 0, suggestionWilliams = 0;

   // --- RSI ---
   int handleRSI = iRSI(Symbol(), PERIOD_CURRENT, 14, PRICE_CLOSE);
   double rsi[1];
   color rsiValueColor = clrGreen; // default
   if(CopyBuffer(handleRSI, 0, 0, 1, rsi) > 0)
   {
      if(rsi[0] < 30)
      {
         rsiValueColor = clrBlue; // buy condition
         suggestionRSI = 1;
      }
      else if(rsi[0] > 70)
      {
         rsiValueColor = clrRed;  // sell condition
         suggestionRSI = -1;
      }
      m_lblRSIValue.Color(rsiValueColor);
      m_lblRSIValue.Text(DoubleToString(rsi[0], 2));
   }
   IndicatorRelease(handleRSI);

   // --- Stochastic ---
   int handleStoch = iStochastic(Symbol(), PERIOD_CURRENT, 5, 3, 3, MODE_SMA, STO_LOWHIGH);
   double stochK[1], stochD[1];
   if(CopyBuffer(handleStoch, 0, 0, 1, stochK) > 0 && CopyBuffer(handleStoch, 1, 0, 1, stochD) > 0)
   {
      // Initialize color variables.
      color colorK = clrGreen;
      color colorD = clrGreen;
      if(stochK[0] > stochD[0])
      {
         colorK = clrBlue;
         colorD = clrRed;
         suggestionStoch = 1;
      }
      else if(stochK[0] < stochD[0])
      {
         colorK = clrRed;
         colorD = clrBlue;
         suggestionStoch = -1;
      }
      else
         suggestionStoch = 0;

      m_lblStochK.Color(colorK);
      m_lblStochD.Color(colorD);
      m_lblStochK.Text("K: " + DoubleToString(stochK[0], 4));
      m_lblStochD.Text("D: " + DoubleToString(stochD[0], 4));
   }
   IndicatorRelease(handleStoch);

   // --- CCI ---
   int handleCCI = iCCI(Symbol(), PERIOD_CURRENT, 14, PRICE_TYPICAL);
   double cci[1];
   color cciValueColor = clrGreen;
   if(CopyBuffer(handleCCI, 0, 0, 1, cci) > 0)
   {
      if(cci[0] < -100)
      {
         cciValueColor = clrBlue; // buy condition
         suggestionCCI = 1;
      }
      else if(cci[0] > 100)
      {
         cciValueColor = clrRed;  // sell condition
         suggestionCCI = -1;
      }
      m_lblCCIValue.Color(cciValueColor);
      m_lblCCIValue.Text(DoubleToString(cci[0], 2));
   }
   IndicatorRelease(handleCCI);

   // --- Williams %R ---
   int handleWPR = iWPR(Symbol(), PERIOD_CURRENT, 14);
   double williams[1];
   color williamsValueColor = clrGreen;
   if(CopyBuffer(handleWPR, 0, 0, 1, williams) > 0)
   {
      if(williams[0] > -80)
      {
         williamsValueColor = clrBlue; // buy condition
         suggestionWilliams = 1;
      }
      else if(williams[0] < -20)
      {
         williamsValueColor = clrRed;  // sell condition
         suggestionWilliams = -1;
      }
      m_lblWilliamsValue.Color(williamsValueColor);
      m_lblWilliamsValue.Text(DoubleToString(williams[0], 2));
   }
   IndicatorRelease(handleWPR);

   // --- Consensus Summary ---
   int consensus = 0;
   if(suggestionRSI != 0 && suggestionRSI == suggestionStoch &&
      suggestionRSI == suggestionCCI && suggestionRSI == suggestionWilliams)
      consensus = suggestionRSI;

   if(consensus == 1)
   {
      m_lblExpectation.Text("Buy");
      m_lblExpectation.Color(clrBlue);
      m_lblConfirmation.Text("↑"); // Up arrow for Buy
      m_lblConfirmation.Color(clrBlue);
   }
   else if(consensus == -1)
   {
      m_lblExpectation.Text("Sell");
      m_lblExpectation.Color(clrRed);
      m_lblConfirmation.Text("↓"); // Down arrow for Sell
      m_lblConfirmation.Color(clrRed);
   }
   else
   {
      m_lblExpectation.Text("No Consensus");
      m_lblExpectation.Color(clrOrangeRed);
      m_lblConfirmation.Text("-");
      m_lblConfirmation.Color(clrOrangeRed);
   }
}

CAnalyticsPanelをメイン管理パネルに統合する

1. EAファイルの先頭には、CAnalyticsPanelクラスへのアクセスを提供するためにAnalyticsPanel.mqhヘッダーファイルが組み込まれています。

#include <AnalyticsPanel.mqh>

これにより、コンパイル時にコンパイラがクラスとそのメンバーを認識するようになります。

2. グローバルポインタを宣言する

CAnalyticsPanel型のグローバルポインタが宣言されており、これはパネルのインスタンスを動的に管理するために使用されます。

CAnalyticsPanel *g_analyticsPanel = NULL;

このポインタは、パネルが存在するかどうかを追跡し、パネルの動的な作成、破棄、および表示の切り替えを可能にします。

3. パネルを起動するボタンを作成する

CreateAdminPanel関数では、「Analytics Panel」というラベルの付いたボタンが作成され、メインの管理パネルに追加されます。このボタンは、ユーザーがパネルを起動するためのトリガーとして機能します。

bool CreateAdminPanel()
{
 
   // Analytics Panel Button
   if(!CreateButton(g_analyticsButton, ANALYTICS_BTN_NAME, INDENT_LEFT, y, INDENT_LEFT+btnWidth, y+BUTTON_HEIGHT, "Analytics Panel"))
      return false;
  
return true;

}

4. ボタンクリックイベントを処理する

HandleAnalytics関数は、パネルのライフサイクルを管理する役割を担います。パネルがすでに作成されているかどうかを確認します。パネルがすでに作成されていない場合は、CAnalyticsPanelをインスタンス化し、位置座標を使用してCreatePanelメソッドを呼び出し、その可視性を切り替えます。それ以外の場合は、パネルのオン/オフを切り替えるだけです。

//------------------------------------------------------------------
// Handle Analytics Panel button click
void HandleAnalytics()
{
   if(g_analyticsPanel == NULL)
   {
      g_analyticsPanel = new CAnalyticsPanel();
      // Coordinates for Analytics panel (adjust as needed)
      if(!g_analyticsPanel.CreatePanel(g_chart_id, "AnalyticsPanel", g_subwin, 900, 20, 900+500, 20+460))
      {
         delete g_analyticsPanel;
         g_analyticsPanel = NULL;
         Print("Failed to create Analytics Panel");
         return;
      }
   }
   g_analyticsPanel.Toggle();
   ChartRedraw();
}

5. イベント転送

AdminPanelOnEvent関数では、クリックやユーザー操作などのイベントが、g_analyticsPanelが表示されている場合はg_analyticsPanelに渡されます。これにより、メインパネルや他のサブパネルと競合することなく、パネルがユーザー入力に独立して応答できるようになります。

if(g_analyticsPanel != NULL && g_analyticsPanel.IsVisible())
      return g_analyticsPanel.OnEvent(id, lparam, dparam, sparam);

6. パネルを動的に更新する

OnTick関数内で分析パネルが存在する場合、そのUpdatePanelメソッドが定期的に呼び出されます。これにより、EAの実行中にパネルに表示される分析またはUIコンテンツを動的に更新できるようになります。

void OnTick()
  {

    if(g_analyticsPanel != NULL)  
    {
      g_analyticsPanel.UpdatePanel();
      
    }
          
  }

7.クリーンアップ

OnDeinit関数では、Destroyを呼び出してポインタを削除することで、パネルが適切に破棄され、メモリが解放されます。これは、EAが削除または再コンパイルされるときにメモリリークを回避するために重要です。


テスト

素晴らしいテスト体験ができました。まるでリアルタイムで市場を見ているように、ティックごとにパネルの値が更新される様子は、非常に臨場感があったことと思います。コンフルエンス戦略を使って実際に取引もできたことは大きな成果です。記録を始めたときにはシグナルが「No Consensus」に変わってしまっていたのは少し残念ですが、それでも結果を共有できるのはとても嬉しいです。

Anlytics Panelと取引New_Admin_Panelのテスト

分析パネルの動作をテストするデモ

上の画像では、ポジションのクローズごとに[Open Trades]の値が更新されました。最初は21ポジションから始まり、最終的には18ポジションになりました。

AnalyticsPanel

すべてのパネルはホームパネルからアクセスできる



結論

分析パネルを統合することでプログラムを無事に拡張できました。この追加により、他のパネルと同様のモジュール方式を用いて、さらに多くのサブパネルを組み込める可能性が示されました。本稿はシリーズの第IX部で締めくくりとなりますが、これで作業が完了したわけではなく、まだ改善すべき点はいくつか残っています。ただし、コアとなるコンセプトは確立され、明確に提示されました。

初心者から経験豊富な開発者まで、幅広い層がこのシリーズから多くの示唆を得られるでしょう。ここで得たアイデアは、今後のさらに高度な開発のための強固な基盤となります。

今回示した結果では、チャートにチャネルラインを引いており、偶然にも分析パネルが買いシグナルを発しました。このシグナルは、すべての指標のコンフルエンス(収束)によるもので、チャネルのサポートゾーンと完全に一致していました。私はこのコンフルエンス戦略に基づき、自信を持って取引をおこないました。テストはデモ口座で実施しています。常に実際の資金をリスクにさらす前にデモ口座で十分にテストすることを強く推奨します。

このセットアップの強みは、トレーダーが分析パネルを通じて重要な市場および口座情報に即座にアクセスできること、さらにコンフルエンスに基づくシグナルの分析力も活用できる点にあります。一方で、戦略のアラート機能が未実装であるため、通知機能を追加すれば、より使いやすくなり、チャンスを見逃すことも減るでしょう。

ヘッダーファイルおよびメインプログラムは以下に添付しています。ぜひコメント欄でご意見やご感想をお聞かせください。次回の公開まで、開発をお楽しみください。取引の成功を祈っています。

ファイル名 説明
AnalyticsPanel.mqh 複数のインジケーターを集約して合流ベースの取引シグナルを生成する分析パネルの構造と動作を定義するヘッダーファイル
New_Admin_Panel.mqh ナビゲーション、認証、コミュニケーション、分析、取引管理などのさまざまなサブパネルの統合など、管理パネルインターフェイス全体を初期化および管理するメインのMQL5 EAプログラム

目次に戻る

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

添付されたファイル |
AnalyticsPanel.mqh (32.77 KB)
New_Admin_Panel.mq5 (19.61 KB)
最後のコメント | ディスカッションに移動 (1)
amrhamed83
amrhamed83 | 16 4月 2025 において 11:17
すべてのファイルを添付ファイルにまとめて送ってもらえますか?
MQL5での取引戦略の自動化(第14回):MACD-RSI統計手法を用いた取引レイヤリング戦略 MQL5での取引戦略の自動化(第14回):MACD-RSI統計手法を用いた取引レイヤリング戦略
この記事では、MACDおよびRSIインジケーターと統計的手法を組み合わせた取引レイヤリング戦略を紹介します。このアプローチは、MQL5による自動売買において、ポジションを動的にスケーリングすることを目的としています。カスケード構造による戦略のアーキテクチャを解説し、主要なコードセグメントを通じて実装方法を詳述します。さらに、パフォーマンスを最適化するためのバックテスト手順についても案内します。最後に、この戦略が持つ可能性と、今後の自動売買戦略への発展性について考察します。
MQL5入門(第15回):初心者のためのカスタムインジケーター作成ガイド(IV) MQL5入門(第15回):初心者のためのカスタムインジケーター作成ガイド(IV)
この記事では、MQL5でプライスアクションインジケーターを構築する方法を学びます。具体的には、トレンド分析において重要なポイントである、安値(L)、高値(H)、安値切り上げ(HL)、高値更新(HH)、安値更新(LL)、高値切り下げ(LH)といった構造の把握に焦点を当てます。また、プレミアムゾーンとディスカウントゾーンの識別、50%リトレースメントレベルの表示、リスクリワード比に基づく利益目標の計算についても解説します。さらに、トレンド構造に基づいてエントリーポイント、ストップロス(SL)、テイクプロフィット(TP)の設定方法も扱います。
プライスアクション分析ツールキットの開発(第19回):ZigZag Analyzer プライスアクション分析ツールキットの開発(第19回):ZigZag Analyzer
すべてのプライスアクショントレーダーは、トレンドを確認し、転換点や継続の可能性があるレベルを見つけるために、トレンドラインを手動で使用します。本連載では、市場分析を簡単にするために、傾斜トレンドラインを描画することに特化したツールを紹介します。このツールは、トレーダーが効果的なプライスアクション評価に不可欠な主要トレンドとレベルを明確に示すことで、分析プロセスを簡素化します。
ペア取引における平均回帰による統計的裁定取引:数学で市場を攻略する ペア取引における平均回帰による統計的裁定取引:数学で市場を攻略する
本記事では、ポートフォリオレベルの統計的アービトラージの基本的な概念を紹介します。数学の深い知識がない読者にも理解しやすく説明し、実際の運用を始めるためのコンセプトフレームワークを提案することを目的としています。記事には、動作するエキスパートアドバイザー(EA)と、1年間のバックテストに関する注記、再現用の設定ファイル(.iniファイル)も含まれています。