English Deutsch
preview
MQL5で取引管理者パネルを作成する(第8回):分析パネル

MQL5で取引管理者パネルを作成する(第8回):分析パネル

MetaTrader 5 | 9 4月 2025, 09:15
86 0
Clemence Benjamin
Clemence Benjamin

はじめに

この記事では、管理パネルEAにおける3番目のサブパネルの開発について説明し、現状の制限を打破しながら、その機能をさらに強化することを目指します。既存の設計ではすでにコミュニケーションと取引管理が可能ですが、今回のアップデートでは、重要な市場指標の分析を効率化するための統計ツールが新たに導入されます。これらのツールは、調査や計算の自動化により、従来の手動作業への依存を排除し、取引管理者の作業を大幅に簡素化します。PieChartによって視覚化されたデータのシンプルさと明瞭さに着想を得て、本稿では勝敗比率と取引タイプの分類という、取引パフォーマンスに関する2つの重要な指標に注目します。これらの指標は、取引の成功度合いや、FX・株式・オプションなどの資産クラスごとの取引配分を即座に把握するのに役立ちます。

分析パネルはリアルタイムのデータ視覚化を活用し、手動分析の非効率性を解消します。円グラフを取り入れることで、ユーザーは勝敗比率や取引タイプの分布を迅速に評価でき、意思決定のスピードと精度が向上します。この機能により、取引管理者はより正確かつ迅速な判断を下すことが可能になります。

この開発では、MQL5のPieChartクラスおよびChartCanvasクラスを活用し、これらの分析プロセスを自動化し、高度な統計ツールの可能性を示すとともに、管理パネルEAの機能を一層強化します。本連載の教育的かつ実践的な価値を強調しながら、視覚的に価値あるインサイトを即座に提供できる点も特徴です。

プロジェクトを成功させるために、コアコンテンツとして次のサブトピックを用意しました。

  1. 分析パネルの概要
  2. CDialogクラスを使用した分析パネルの準備
  3. 取引履歴データの取得と表示
  4. データを表示するためのPieChartクラスとChartCanvasクラスの実装
  5. 新機能のテスト
  6. 結論

分析パネルの概要

分析パネルは、取引パフォーマンスやアクティビティの分布を視覚的に把握できる、動的かつインタラクティブなインターフェイスとして設計されています。本稿では、このパネルに実装された2つの主要な円グラフに注目します。ひとつは「勝ち vs 負け」円グラフで、勝ち取引と負け取引の比率を示し、もうひとつは「取引タイプ別分布グラフ」で、取引を外国為替・株式・先物の3カテゴリに分類します。これらのグラフはパネルにシームレスに統合されており、直感的で洗練されたレイアウトによって、データの解釈を容易にします。取引履歴から取得したリアルタイムデータを活用することで、分析パネルは取引結果を包括的に俯瞰できるスナップショットを提供し、ユーザーが自身のパフォーマンスをすぐに把握できるようになります。

さらにこのパネルは、以下のようなビジュアライゼーションや指標を追加することで、より詳細で多角的な分析が可能になります。組み込むことができる機能は次のとおりです。

  • パフォーマンスラインチャート
  • 取引量棒グラフ
  • 収益性指標表 
  • トップパフォーマンス資産のセクション
  • 取引時間のヒートマップ
  • 勝ち負け記録トラッカー
  • リスクエクスポージャーチャート
  • カスタマイズ可能なアラートと閾値
  • 感情分析の統合
  • インタラクティブフィルターなど

つまり、分析パネルは取引全体のパフォーマンスを多角的に可視化することで、ユーザーが取引傾向を把握し、収益性を評価し、進捗を時系列で追跡することを可能にします。これにより、トレーダーは自らの強みと弱点を客観的に認識し、データに基づいた戦略の見直しをおこなえるようになります。それでは次に、既存の管理パネルをアップグレードするための準備段階に進みます。また、これまでのコードもすべて見直し、より理解しやすく整えました。



CDialogクラスを使用した分析パネルの準備

大規模なプログラムのアップグレードは、特に過去におこなった作業を繰り返す必要がある場合、圧倒される思いがすることも少なくありません。だからこそ、プログラムのレイアウトや構造を統一的に管理する、明確に定義されたテンプレートの存在が重要になります。一度堅牢なテンプレートを構築し、繰り返し活用することで、開発フローに自然と組み込まれ、都度参照することなく全体の開発タスクを効率よく進められるようになります。実際、私たちのプログラムは大幅に拡張されており、その複雑さを整理・管理するために、「何を実現したいのか」と「それがどのように流れていくか」という2点に常に意識を向けています。そして、その目的を各モジュールと照らし合わせながら開発を進めています。

たとえば、プログラム起動後の中核インターフェイスとして機能する「管理ホームパネル」(Admin Home Panel,)があります。ここから他のパネルにアクセス可能です。新たに分析パネルを追加する場合、管理ホームパネル内にボタンを設置し、それをクリックすることで分析パネルが生成・表示されるようにすることを想定しています。分析パネル内には、その目的に特化した操作ボタンや機能を配置します。このように開発ビジョンを明確にイメージすることで、プロセス全体に一貫性と方向性が生まれ、自然と開発の出発点も明確になります。次に、私たちが適用したアプローチについて紹介します。

ここから本題に入ります。

まずは、使用する必要なクラスの組み込みです。

#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>

以下は、ダイアログと分析パネルのボタンの宣言です。

// Global variables for the Analytics Panel
CDialog analyticsPanel;                      // The main panel for analytics
CButton adminHomeAnalyticsButton;            // Button for accessing analytics from the Home panel
CButton minimizeAnalyticsButton;             // Minimize button for the Analytics panel
CButton closeAnalyticsButton;                // Close button for the Analytics panel
CButton analyticsPanelAccessButton;          // The first Button that will take us a next step

以下は、分析パネルの作成とボタンハンドラです。

ボタンハンドラ関数を使用して、analyticsPanelAccessButtonをクリックしたときに分析パネルを作成する機能を実装しました。同様のアプローチが他のパネルにも適用されています。以前は、これらのパネルは初期化フェーズ中に作成され、非表示になっていたため、初期化関数に不要な負荷がかかっていました。パネルはそれぞれのボタンのクリックを通じてオンデマンドで動的に作成され、パフォーマンスとリソースの使用が最適化されるようになりました。以下は、この改善を示すコードスニペットです。

//+------------------------------------------------------------------+
//| Analytics Panel Event Handling                                   |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if (id == CHARTEVENT_OBJECT_CLICK)
    {
        if (sparam == "AnalyticsPanelAccessButton")
        {
            analyticsPanel.Show();
            adminHomePanel.Hide();
            if (!analyticsPanel.Create(ChartID(), "Analytics Panel", 0, 500, 450, 1280, 650) || !CreateAnalyticsPanelControls()) {}
            CreateAnalyticsPanel();
        }
        else if (sparam == "MinimizeAnalyticsButton")
        {
            analyticsPanel.Hide();
            adminHomePanel.Show();
        }
        else if (sparam == "CloseAnalyticsButton")
        {
            analyticsPanel.Destroy();
            adminHomePanel.Show();
        }
    }
}

このコードスニペットは、OnChartEvent関数内の分析パネルに関連するイベント、具体的にはチャートオブジェクトでユーザーのクリックが検出されたとき(CHARTEVENT_OBJECT_CLICK)に処理します。[Analytics Panel Access]ボタンをクリックすると、パネルが表示され、管理ホームパネルは非表示になり、CreateAnalyticsPanel関数を使用して、分析パネルとそのコントロールが動的に作成されます。[Minimize Analytics Button]ボタンをクリックすると、分析パネルが非表示になり、管理ホームパネルが再び表示されます。最後に、[Close Analytics]ボタンをクリックすると、分析パネルが完全に破棄され、管理ホームパネルが画面に復元されます。この動的な処理により、ユーザーのアクションに基づいて適切なパネルが表示または非表示になり、機能性とユーザーエクスペリエンスの両方が向上します。

次は、分析パネルコントロールの作成です。

以下のコードスニペットは、MQL5アプリケーションの分析パネルを初期化してコントロールを追加するCreateAnalyticsPanelControls関数を定義します。まずChartID()を使用して現在のチャートのIDを取得し、特定の座標に最小化ボタン(minimizeAnalyticsButton)の作成を試みします。作成に失敗した場合は、エラーメッセージがログに記録され、falseが返されます。成功した場合、ボタンにアンダースコア(「_」)のラベルが付けられ、analyticsPanelコンテナに追加されます。同様に、同じエラーチェックプロセスに従って、別の座標セットに「X」のラベルが付いた閉じるボタン(closeAnalyticsButton)を作成します。この関数は、グラフや入力要素などの追加の分析関連のコントロールを追加できる場所を示すプレースホルダーコメントで終了します。すべてのコントロールが正常に作成された場合、関数はtrueを返します。

bool CreateAnalyticsPanelControls()
{
    long chart_id = ChartID();

    // Create Minimize Button
    if (!minimizeAnalyticsButton.Create(chart_id, "MinimizeAnalyticsButton", 0, 210, -22, 240, 0))
    {
        Print("Failed to create minimize button for Analytics Panel");
        return false;
    }
    minimizeAnalyticsButton.Text("_");
    analyticsPanel.Add(minimizeAnalyticsButton);

    // Create Close Button
    if (!closeAnalyticsButton.Create(chart_id, "CloseAnalyticsButton", 0, 240, -22, 270, 0))
    {
        Print("Failed to create close button for Analytics Panel");
        return false;
    }
    closeAnalyticsButton.Text("X");
    analyticsPanel.Add(closeAnalyticsButton);

    // Add additional controls specific to analytics as needed
    // For example, charts, labels, or input elements for data representation

    return true;
}

また、多くのパネルの作成を初期化関数から移動して、ボタンハンドラを通じてトリガーされるようにしました。この変更をおこなってから、エキスパートアドバイザー(EA)のパフォーマンス、特にさまざまなパネル間を移動する際の速度が大幅に向上したことに気づきました。

主なOnChartEventハンドラ関数の組み合わせは次のとおりです。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if (id == CHARTEVENT_OBJECT_CLICK)
    {
        if (sparam == "HomeButtonComm") { adminHomePanel.Show(); communicationsPanel.Hide(); }
        else if (sparam == "HomeButtonTrade") { adminHomePanel.Show(); tradeManagementPanel.Hide(); }
        else if (sparam == "AdminHomeAnalyticsButton") { adminHomePanel.Show(); analyticsPanel.Hide(); }
        else if (sparam == "MinimizeAnalyticsButton") { analyticsPanel.Hide(); adminHomePanel.Show(); }
        else if (sparam == "CloseAnalyticsButton") { analyticsPanel.Destroy(); adminHomePanel.Show(); }
        else if (sparam == "TradeMgmtAccessButton") {
            tradeManagementPanel.Show(); adminHomePanel.Hide();
            if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 500, 30, 1280, 170) || !CreateTradeManagementControls()) {}
        }
        else if (sparam == "CommunicationsPanelAccessButton") {
            communicationsPanel.Show(); adminHomePanel.Hide();
            if (!communicationsPanel.Create(ChartID(), "Communications Panel", 0, 20, 150, 490, 650) || !CreateCommunicationsPanelControls()) {}
        }
        else if (sparam == "CloseHomeButton") { adminHomePanel.Destroy(); }
        else if (sparam == "MinimizeHomeButton") { adminHomePanel.Hide(); maximizeHomeButton.Show(); }
        else if (sparam == "MaximizeHomeButton") { adminHomePanel.Show(); maximizeHomeButton.Show(); }
        else if (sparam == "AnalyticsPanelAccessButton") {
            analyticsPanel.Show(); adminHomePanel.Hide();
            if (!analyticsPanel.Create(ChartID(), "Analytics Panel", 0, 500, 450, 1280, 650) || !CreateAnalyticsPanelControls()) {};
          CreateAnalyticsPanel();
        }
        
        else if (sparam == "ShowAllButton") {
            analyticsPanel.Show(); communicationsPanel.Show(); tradeManagementPanel.Show(); adminHomePanel.Hide();
        }
        else if (sparam == "MinimizeComsButton") { OnMinimizeComsButtonClick(); }
        else if (sparam == "CloseComsButton") { communicationsPanel.Destroy(); }
        else if (sparam == "LoginButton") { OnLoginButtonClick(); }
        else if (sparam == "CloseAuthButton") { OnCloseAuthButtonClick(); }
        else if (sparam == "TwoFALoginButton") { OnTwoFALoginButtonClick(); }
        else if (sparam == "Close2FAButton") { OnClose2FAButtonClick(); }
    }

    switch (id)
    {
        case CHARTEVENT_OBJECT_CLICK:
            if (sparam == "SendButton") OnSendButtonClick();
            else if (sparam == "ClearButton") OnClearButtonClick();
            else if (sparam == "ChangeFontButton") OnChangeFontButtonClick();
            else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick();
            else if (sparam == "MinimizeComsButton") OnMinimizeComsButtonClick();
            else if (sparam == "CloseComsButton") OnCloseComsButtonClick();
            else if (StringFind(sparam, "QuickMessageButton") != -1) {
                long index = StringToInteger(StringSubstr(sparam, 18));
                OnQuickMessageButtonClick(index - 1);
            }
            break;

        case CHARTEVENT_OBJECT_ENDEDIT:
            if (sparam == "InputBox") OnInputChange();
            break;
    }
}

最後に、以下のコードスニペットの座標と幅に示されているように、管理ホームパネルを調整することも検討しました。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
        
    if (!ShowAuthenticationPrompt())
    {
        Print("Authorization failed. Exiting...");
        return INIT_FAILED;
    }

    if (!adminHomePanel.Create(ChartID(), "Admin Home Panel", 0, 30, 80,330, 550))
    {
        Print("Failed to create Admin Home Panel");
        return INIT_FAILED;
    }

    if (!CreateAdminHomeControls())
    {
        Print("Home panel control creation failed");
        return INIT_FAILED;
    }
    
    adminHomePanel.Hide(); // Hide home panel by default on initialization
    
    return INIT_SUCCEEDED;
}


取引履歴データの取得と表示

ここで、新しいパネルの円グラフで表示できるデータを端末履歴から取得する必要があります。GetTradeData関数は、過去の取引データを分析し、特定のカテゴリに分類して、詳細な取引パフォーマンス分析の基盤を提供するように設計されています。まず、勝ち、負け、および3つの取引タイプ(外国為替、株式、先物)のカウンタを初期化します。この関数は、HistorySelect関数を利用して、取引口座の履歴の最初から現在の時刻までの取引データを取得します。この選択プロセスが失敗した場合、関数はエラーをログに記録して終了し、利用できない履歴データに対する堅牢性を確保します。次に、HistoryDealGetTicketを使用して利用可能なすべての取引を反復処理し、各取引の一意の識別子を取得します。有効な取引ごとに、関数は利益値を分析してその収益性を評価し、利益がプラスかマイナスかに基づいて勝ちまたは負けのカウンタを増分します。

取引の分類は銘柄によって決まります。外国為替取引は銘柄名にドットがないことによって識別されますが、株式および先物取引は銘柄のSYMBOL_PATHプロパティをチェックすることによって分類されます。銘柄のグループ名によって、それが「株式」カテゴリと「先物」カテゴリのどちらに該当するかが決まります。このステップにより、取引が金融商品ごとに正確にグループ化されることが保証されます。この情報を集約することで、この関数は取引パフォーマンスの包括的な内訳を提供し、さらなる分析や視覚化に使用できます。

今日のシナリオでは、この関数を分析パネルに統合して、取引カテゴリの分布と勝ちと負けの比率を示す円グラフを生成できます。たとえば、トレーダーはこの関数を使用して、自分の取引の60%が外国為替、30%が株式、10%が先物であり、全体の成功率が70%であることを視覚化することができます。このような洞察は、取引パフォーマンスを評価し、改善すべき領域を特定するために非常に貴重です。さらに、この関数のリアルタイムデータ分析機能により、トレーダーが過去の傾向に基づいて戦略を適応させるのに役立つ応答性の高いダッシュボードを作成するのに適しています。

GetTradeData関数は、現在の実装を超えて、より幅広い用途に使用できる可能性があります。拡張して、特定の時間範囲を分析したり、平均利益やドローダウンなどの追加のメトリックを組み込んだりすることもできます。このデータは、予測分析用の機械学習モデルや投資家向けプレゼンテーション用のインタラクティブレポートなどの外部ツールに統合できます。このような拡張機能により、この関数は、効率性と収益性の最大化を目指す個人トレーダーと大規模取引システムの両方にとって多目的なツールになります。

実装されたコードは次のとおりです。

//+------------------------------------------------------------------+
//| Data for Pie Chart                                               |
//+------------------------------------------------------------------+

void GetTradeData(int &wins, int &losses, int &forexTrades, int &stockTrades, int &futuresTrades) {
    wins = 0;
    losses = 0;
    forexTrades = 0;
    stockTrades = 0;
    futuresTrades = 0;

    if (!HistorySelect(0, TimeCurrent())) {
        Print("Failed to select trade history.");
        return;
    }

    int totalDeals = HistoryDealsTotal();

    for (int i = 0; i < totalDeals; i++) {
        ulong dealTicket = HistoryDealGetTicket(i);
        if (dealTicket > 0) {
            double profit = HistoryDealGetDouble(dealTicket, DEAL_PROFIT);

            if (profit > 0) wins++;
            else if (profit < 0) losses++;

            string symbol = HistoryDealGetString(dealTicket, DEAL_SYMBOL);
            if (SymbolInfoInteger(symbol, SYMBOL_SELECT)) {
                if (StringFind(symbol, ".") == -1) forexTrades++;
                else {
                    string groupName;
                    if (SymbolInfoString(symbol, SYMBOL_PATH, groupName)) {
                        if (StringFind(groupName, "Stocks") != -1) stockTrades++;
                        else if (StringFind(groupName, "Futures") != -1) futuresTrades++;
                    }
                }
            }
        }
    }
}


データを表示するためのPieChartクラスとChartCanvasクラスの実装

まずクラスをインクルードします。

#include <Canvas\Charts\PieChart.mqh>
#include <Canvas\Charts\ChartCanvas.mqh>

カスタム円グラフクラス

まず、CPieChart基本クラスからCCustomPieChartクラスを派生させました。目標は、通常は親クラスの外部からはアクセスできない、protectedメソッド「DrawPie」を公開することでした。DrawPieをラップするpublicメソッド「DrawPieSegment」を作成することで、個々の円グラフのセグメントを動的に描画する柔軟性が得られました。これは、CAnalyticsChartクラスのDrawPieChartメソッドでカスタムレンダリングロジックを実装するときに特に役立ちました。このステップにより、各円グラフのスライスの視覚的表現を細かく制御できるようになり、分析パネル用により動的で視覚的にカスタマイズされた円グラフを作成できるようになりました。

//+------------------------------------------------------------------+
//| Custom Pie Chart Class                                           |
//+------------------------------------------------------------------+
class CCustomPieChart : public CPieChart {
public:
    void DrawPieSegment(double fi3, double fi4, int idx, CPoint &p[], const uint clr) {
        DrawPie(fi3, fi4, idx, p, clr); // Expose protected method
    }
};

分析チャートクラス

次に、CWndクラスを拡張して、特殊なチャートコンテナであるCAnalyticsChartを作成しました。このクラスでは、CCustomPieChartをメンバーとして統合し、円グラフを描画するための基盤として機能できるようにしています。定義された領域内で円グラフウィジェットを初期化するCreatePieChartや、データ値、ラベル、色をグラフにリンクするSetPieChartDataなどのメソッドを実装しました。さらに、DrawPieChartメソッドは、データセットに基づいて各セグメントの角度スパンを計算し、レンダリングのためにDrawPieSegmentを呼び出すように慎重にコーディングされました。このロジックを実行することで、円グラフを動的に描画し、視覚的に魅力的な方法で基礎となるデータを反映しられるようになりました。

//+------------------------------------------------------------------+
//| Analytics Chart Class                                            |
//+------------------------------------------------------------------+
class CAnalyticsChart : public CWnd {
private:
    CCustomPieChart pieChart;  // Declare pieChart as a member of this class

public:
    bool CreatePieChart(string label, int x, int y, int width, int height) {
        if (!pieChart.CreateBitmapLabel(label, x, y, width, height)) {
            Print("Error creating Pie Chart: ", label);
            return false;
        }
        return true;
    }

    void SetPieChartData(const double &values[], const string &labels[], const uint &colors[]) {
        pieChart.SeriesSet(values, labels, colors);
        pieChart.ShowPercent();
    }

    void DrawPieChart(const double &values[], const uint &colors[], int x0, int y0, int radius) {
        double total = 0;
        int seriesCount = ArraySize(values);

        if (seriesCount == 0) {
            Print("No data for pie chart.");
            return;
        }

        for (int i = 0; i < seriesCount; i++)
            total += values[i];

        double currentAngle = 0.0;

        // Resize the points array
        CPoint points[];
        ArrayResize(points, seriesCount + 1);

        for (int i = 0; i < seriesCount; i++) {
            double segmentValue = values[i] / total * 360.0;
            double nextAngle = currentAngle + segmentValue;

            // Define points for the pie slice
            points[i].x = x0 + (int)(radius * cos(currentAngle * M_PI / 180.0));
            points[i].y = y0 - (int)(radius * sin(currentAngle * M_PI / 180.0));

            pieChart.DrawPieSegment(currentAngle, nextAngle, i, points, colors[i]);

            currentAngle = nextAngle;
        }

        // Define the last point to close the pie
        points[seriesCount].x = x0 + (int)(radius * cos(0));  // Back to starting point
        points[seriesCount].y = y0 - (int)(radius * sin(0));
    }
};

分析パネル機能の作成

すべてを結び付けるために、分析パネルの実際の実装を処理するCreateAnalyticsPanel関数を作成しました。まず、GetTradeData関数を使用して、勝ち、負け、取引タイプの数などの取引データを取得しました。次に、異なる視覚化のために2つのCAnalyticsChartオブジェクトをインスタンス化しました。最初のグラフでは、取得した勝敗データを使用して、「Win vs. Loss Pie Chart」というラベルの付いた円グラフを作成しました。同様に、2番目のグラフでは、取引タイプのデータを使用して、「取引タイプの分布」円グラフを作成しました。各チャートに対してSetPieChartDataDrawPieChartを呼び出すことで、チャートを動的にレンダリングし、analyticsPanelに追加しました。このアプローチにより、コードをモジュール式の再利用可能なコンポーネントに分割し、明確さと保守性を確保できました。

//+------------------------------------------------------------------+
//| Create Analytics Panel                                           |
//+------------------------------------------------------------------+
void CreateAnalyticsPanel() {
    int wins, losses, forexTrades, stockTrades, futuresTrades;
    GetTradeData(wins, losses, forexTrades, stockTrades, futuresTrades);

    // Declare pieChart1 and pieChart2 as local variables
    CAnalyticsChart pieChart1;
    CAnalyticsChart pieChart2;

    // Win vs Loss Pie Chart
    if (!pieChart1.CreatePieChart("Win vs. Loss Pie Chart", 20, 20, 300, 300)) {
        Print("Error creating Win/Loss Pie Chart");
        return;
    }

    double winLossValues[] = {wins, losses};
    string winLossLabels[] = {"Wins", "Losses"};
    uint winLossColors[] = {clrGreen, clrRed};

    pieChart1.SetPieChartData(winLossValues, winLossLabels, winLossColors);
    pieChart1.DrawPieChart(winLossValues, winLossColors, 150, 150, 140);

    // Add pieChart1 to the analyticsPanel
    analyticsPanel.Add(pieChart1);

    // Trade Type Pie Chart
    if (!pieChart2.CreatePieChart("Trade Type Distribution", 350, 20, 300, 300)) {
        Print("Error creating Trade Type Pie Chart");
        return;
    }

    double tradeTypeValues[] = {forexTrades, stockTrades, futuresTrades};
    string tradeTypeLabels[] = {"Forex", "Stocks", "Futures"};
    uint tradeTypeColors[] = {clrBlue, clrOrange, clrYellow};

    pieChart2.SetPieChartData(tradeTypeValues, tradeTypeLabels, tradeTypeColors);
    pieChart2.DrawPieChart(tradeTypeValues, tradeTypeColors, 500, 150, 140);

    // Add pieChart2 to the analyticsPanel
    analyticsPanel.Add(pieChart2);

    // Show the analyticsPanel
    analyticsPanel.Show();
}

このようにした理由

このようにシステムをコーディングすることで、チャートの作成が動的かつ柔軟になることが保証されました。CCustomPieChartを派生させることで円グラフのレンダリングを制御できるようになり、CAnalyticsChartを使用すると円グラフの機能を自己完結型クラスにカプセル化できるようになりました。これにより、プログラムの他の部分に影響を与えることなく、新しいチャートを追加したり、チャートの動作を調整したりすることが容易になりました。たとえば、今日のプロジェクトで、株式曲線分析用の別のチャートを追加したい場合、最小限の労力で同じCAnalyticsChart構造体を再利用できます。このモジュール式のアプローチは、開発を効率化するだけでなく、分析パネルを将来の機能強化のために高度に拡張可能にします。

配列の範囲外エラーの防止

PieChart.mqhのCPieChart::DrawPieメソッドで「array out of range」というエラーが発生しないようにインデックス(idx+1)CPoint配列(p[])の境界内にあることを確認する範囲チェックを追加しました。この安全策により、使用前に配列のサイズが適切に設定され、無効な操作が防止されます。インデックスが範囲外の場合、関数は早期に終了し、デバッグ用のエラーメッセージを出力します。さらに、円グラフのレンダリング中に、CPoint配列はすべての円グラフのセグメントを収容できるように適切にサイズ変更され、データ構造が常に計算に十分な大きさであることを保証します。追加された条件「if (idx+1>=ArraySize(p))」は、次のインデックスが有効かどうかをチェックし、有効でない場合はエラーメッセージを出力して、それ以上の処理が行われないように早期に返します。このチェックにより、関数が範囲外の配列要素にアクセスしようとするのを防ぎ、エラーを回避します。

if (idx + 1 >= ArraySize(p)) {
    Print("Array out of range error: idx = ", idx, ", ArraySize = ", ArraySize(p));
    return;
}

EAのテスト中に前述のエラーが発生するのを防ぐために、組み込みの円グラフクラスを変更する必要があったことに注意してください。

//+------------------------------------------------------------------+
//| Draw pie                                                         |
//+------------------------------------------------------------------+
void CPieChart::DrawPie(double fi3, double fi4, int idx, CPoint &p[], const uint clr) {
    // Ensure array index is within bounds
    if (idx + 1 >= ArraySize(p)) {
        Print("Array out of range error: idx = ", idx, ", ArraySize = ", ArraySize(p));
        return;
    }

    //--- draw arc
    Arc(m_x0, m_y0, m_r, m_r, fi3, fi4, p[idx].x, p[idx].y, p[idx + 1].x, p[idx + 1].y, clr);

    //--- variables
    int x3 = p[idx].x;
    int y3 = p[idx].y;
    int x4 = p[idx + 1].x;
    int y4 = p[idx + 1].y;

    //--- draw radii
    if (idx == 0)
        Line(m_x0, m_y0, x3, y3, clr);
    if (idx != m_data_total - 1)
        Line(m_x0, m_y0, x4, y4, clr);

    //--- fill
    double fi = (fi3 + fi4) / 2;
    int xf = m_x0 + (int)(0.99 * m_r * cos(fi));
    int yf = m_y0 - (int)(0.99 * m_r * sin(fi));
    Fill(xf, yf, clr);

    //--- for small pie
    if (fi4 - fi3 <= M_PI_4)
        Line(m_x0, m_y0, xf, yf, clr);
}


新機能のテスト

このセクションでは、機能強化の結果を示し、プログラムの更新バージョンとその機能を紹介します。以下は改善点を示す一連の画像です。まず、再設計された管理ホームパネルと新しく追加されたボタンを紹介します。次に、取引データの分布を視覚化する分析パネルを表示します。次に、管理パネルの完全なビューが表示され、そのサブパネルがすべて表示されます。最後に、シームレスな統合のために、ターミナルチャート上でのアプリケーションの展開を示すアニメーションスクリーンキャプチャを含めます。

新しい管理者ホーム

新しい管理者ホームパネル

分析パネル

分析パネル

管理パネルV1.23フルビュー

管理パネルV1.23フルビュー

ADMINPANELV1.24のテスト

Boom 300 Index、H4:管理パネルV1.24EAリリース


結論

本日のディスカッションでは、管理パネルとその機能を強化するための新しいクラスとテクニックの統合に焦点を当てて、MQL5の高度な機能の開発について検討しました。MQL5の機能を活用することで、取引パフォーマンスを効果的に表現する動的なパネルや円グラフなどのデータ駆動型のビジュアルを実装することができました。このプロジェクトは、トレーダーと開発者の両方に適した、洗練されたユーザーフレンドリーなツールを作成するためのMQL5の大きな可能性を示しています。

新たに実装された分析パネルは、トレーダーに対して取引パフォーマンスに関する実用的な洞察を提供するとともに、開発者にとっては拡張性の高い堅牢なフレームワークとなります。パネルの煩雑化、オブジェクトのレイヤー管理、動的コントロールの作成といった課題に対応することで、より効率的かつ直感的なインターフェイスの基盤を築くことができました。これらの改善は、将来的なイノベーションへの足がかりとなります。開発者はこのフレームワークを拡張し、さらなる分析機能やインタラクティブな要素、あるいはまったく新しい機能を組み込むことができます。添付されたソースファイルと画像は、私たちの取り組みの成果を示すものであり、他の方々がMQL5の無限の可能性を探求するためのインスピレーションとなることでしょう。楽しい取引を。このガイドがあなたの創造性を刺激し、プロジェクトをさらに前進させる助けとなることを願っています。

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

添付されたファイル |
PieChart.mqh (13.75 KB)
MQL5とデータ処理パッケージの統合(第4回):ビッグデータの取り扱い MQL5とデータ処理パッケージの統合(第4回):ビッグデータの取り扱い
今回は、MQL5と強力なデータ処理ツールを統合する高度なテクニックに焦点を当て、取引分析および意思決定を強化するためのビッグデータの効率的な活用方法を探ります。
MQL5経済指標カレンダーを使った取引(第4回):ダッシュボードでのリアルタイムニュース更新の実装 MQL5経済指標カレンダーを使った取引(第4回):ダッシュボードでのリアルタイムニュース更新の実装
この記事では、リアルタイムのニュース更新機能を実装することで、経済指標カレンダーダッシュボードを強化し、市場情報を常に最新かつ実用的な状態に保ちます。MQL5におけるライブデータ取得技術を統合し、ダッシュボード上のイベントを継続的に更新することで、インターフェイスの応答性を向上させます。このアップデートにより、ダッシュボードから最新の経済ニュースに直接アクセスでき、最新データに基づいて取引判断を最適化できるようになります。
CatBoost機械学習モデルをトレンド追従戦略のフィルターとして活用する CatBoost機械学習モデルをトレンド追従戦略のフィルターとして活用する
CatBoostは、定常的な特徴量に基づいて意思決定をおこなうことに特化した、強力なツリーベースの機械学習モデルです。XGBoostやRandom Forestといった他のツリーベースモデルも、堅牢性、複雑なパターンへの対応力、そして高い解釈性といった点で共通した特長を備えています。これらのモデルは、特徴量分析からリスク管理に至るまで、幅広い分野で活用されています。本記事では、学習済みのCatBoostモデルを、従来型の移動平均クロスを用いたトレンドフォロー戦略のフィルターとして活用する手順を解説します。戦略構築の過程で直面しうる課題を取り上げながら、具体的な開発プロセスへの理解を深めることを目的としています。MetaTrader 5からのデータ取得、Pythonによる機械学習モデルの学習、そしてそれをMetaTrader 5のエキスパートアドバイザー(EA)へ統合するまでのワークフローをご紹介します。記事の終盤では、統計的検証を通じて戦略の有効性を確認し、現在のアプローチをもとにした今後の展望についても考察していきます。
知っておくべきMQL5ウィザードのテクニック(第50回):Awesome Oscillator 知っておくべきMQL5ウィザードのテクニック(第50回):Awesome Oscillator
Awesome Oscillatorは、モメンタム(勢い)を測定するために使用されるビル・ウィリアムズのインジケーターの一つです。複数のシグナルを生成できるため、以前の記事と同様に、MQL5ウィザードクラスとアセンブリを活用して、パターンベースでこれらを確認します。