
MQL5で取引管理者パネルを作成する(第9回):コード編成(V):AnalyticsPanelクラス
内容
はじめに
前のセクションでは、分析パネルのコンセプトを紹介しましたが、その時点では主に、勝敗比率を表示する円グラフのような静的なインターフェイス要素で構成されていました。しかし、これらのコンポーネントはリアルタイムのデータ更新がなかったため、動的な取引環境では使い勝手が限られていました。本稿では、インターフェイスの設計と機能性の両方を大きく進化させ、リアルタイムの取引データの取得と表示に焦点を当てて大きな進歩を目指します。
私たちの開発手法は、MQL5標準ライブラリ、特に\Include\Controls\ディレクトリにあるクラスを活用します。既存のクラスであるCLabel、CEdit、CDialogなどを拡張・カスタマイズし、応答性が高くデータ駆動型のUIコンポーネントを作成します。これらは、総合的な口座管理や戦略管理を提供し、Telegramを介したリモート操作やリアルタイム通知のためのコミュニケーションインターフェイスも備えた、モジュラーかつ多機能な取引管理パネルの中核を成します。
本稿の主目的は高度なパネルの開発ですが、ここで紹介する技術、特にリアルタイムデータ取得と動的UI更新の手法は、管理インターフェイスに限らず、エキスパートアドバイザー(EA)やカスタムインジケーター、インタラクティブな学習ツールなど、さまざまなMQL5プロジェクトに応用できます。
AnalyticsPanelクラスの概要
大規模なMQL5プログラムに適したモジュラー開発アプローチの一環として、またコードの再利用性と保守性を高めるために、専用のAnalyticsPanelクラスのヘッダーファイルを作成しています。このクラスは、アナリティクスパネルの視覚的なレイアウトと、リアルタイムの市場データ取得および表示の両方をカプセル化することを目的としています。
パネルは標準的な口座指標の表示に加えて、私が「コンフルエンス戦略」と名付けたカスタム戦略に基づく様々なテクニカル指標の値も表示します。この戦略は、複数のインジケーターからのシグナルを比較して統一的な取引シグナルを生成する「コンフルエンス(収束)」の原理に基づいています。もし指標間で合意が得られない場合、パネルには「No Consensus」のメッセージが表示され、誤検出や弱いシグナルを避ける設計となっています。
AnalyticsPanelクラスには、パネルレイアウトの初期化と更新、ラベル値のリアルタイム更新、そして戦略ロジックに基づく視覚的なシグナルフィードバックの管理といったメソッドが含まれます。以下にパネルの視覚デザインレイアウトを示し、その後の解説で実装の詳細について順を追って説明していきます。
AnalyticsPanelの機能
開発の継続的な取り組みの一環として、クラスを開発した後、それをメインのEAであるNew_Admin_Panelプログラムに統合します。この統合はシステムのモジュラーアーキテクチャを強化するとともに、再利用可能で独立したコンポーネントを構築し、それらが大規模なアプリケーションフレームワーク内でシームレスに連携できることの利点を示します。以下の画像は、すべてのインジケーター値が一致し、強い合意を形成した場合にのみ売買シグナルが生成される、完成した製品の最終的なパフォーマンスを示します。
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は現在未決の取引の数を表示します。
- m_lblPnLName:「PnL:」というテキストを単に表示する静的ラベル(黒のまま)
- 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つの部分に分かれています。
- m_lblStochName:静的部分("Stoch:")
- 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は、すべてのインジケーターから得られたコンセンサスシグナルを表示するために使用されます。
- m_lblExpectationは、「Buy」「Sell」「No Consensus」といったテキストによるシグナルの要約を表示します。
- 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」に変わってしまっていたのは少し残念ですが、それでも結果を共有できるのはとても嬉しいです。
分析パネルの動作をテストするデモ
上の画像では、ポジションのクローズごとに[Open Trades]の値が更新されました。最初は21ポジションから始まり、最終的には18ポジションになりました。
すべてのパネルはホームパネルからアクセスできる
結論
分析パネルを統合することでプログラムを無事に拡張できました。この追加により、他のパネルと同様のモジュール方式を用いて、さらに多くのサブパネルを組み込める可能性が示されました。本稿はシリーズの第IX部で締めくくりとなりますが、これで作業が完了したわけではなく、まだ改善すべき点はいくつか残っています。ただし、コアとなるコンセプトは確立され、明確に提示されました。
初心者から経験豊富な開発者まで、幅広い層がこのシリーズから多くの示唆を得られるでしょう。ここで得たアイデアは、今後のさらに高度な開発のための強固な基盤となります。
今回示した結果では、チャートにチャネルラインを引いており、偶然にも分析パネルが買いシグナルを発しました。このシグナルは、すべての指標のコンフルエンス(収束)によるもので、チャネルのサポートゾーンと完全に一致していました。私はこのコンフルエンス戦略に基づき、自信を持って取引をおこないました。テストはデモ口座で実施しています。常に実際の資金をリスクにさらす前にデモ口座で十分にテストすることを強く推奨します。
このセットアップの強みは、トレーダーが分析パネルを通じて重要な市場および口座情報に即座にアクセスできること、さらにコンフルエンスに基づくシグナルの分析力も活用できる点にあります。一方で、戦略のアラート機能が未実装であるため、通知機能を追加すれば、より使いやすくなり、チャンスを見逃すことも減るでしょう。
ヘッダーファイルおよびメインプログラムは以下に添付しています。ぜひコメント欄でご意見やご感想をお聞かせください。次回の公開まで、開発をお楽しみください。取引の成功を祈っています。
ファイル名 | 説明 |
---|---|
AnalyticsPanel.mqh | 複数のインジケーターを集約して合流ベースの取引シグナルを生成する分析パネルの構造と動作を定義するヘッダーファイル |
New_Admin_Panel.mqh | ナビゲーション、認証、コミュニケーション、分析、取引管理などのさまざまなサブパネルの統合など、管理パネルインターフェイス全体を初期化および管理するメインのMQL5 EAプログラム |
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/17397
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。






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