English Русский 中文 Español Deutsch Português
preview
MQL5で取引管理者パネルを作成する(第1回):メッセージングインターフェイスの構築

MQL5で取引管理者パネルを作成する(第1回):メッセージングインターフェイスの構築

MetaTrader 5 |
418 3
Clemence Benjamin
Clemence Benjamin

はじめに

MetaTrader 5からシステムユーザーと直接やり取りすることが可能です。これは、管理者がシステムのパフォーマンスに関する洞察をリアルタイムで共有できる方法のひとつです。また、ソーシャルチャンネルに最近配信されたシステムシグナルの検証としても機能します。最後まで読めば、この画像にあるようなインタラクティブなインターフェイスを作ることが可能であることがおわかりいただけるでしょう。

システム管理パネル

システム管理者パネル:Boom 500指数、上半期


管理パネル

管理者コミュニケーションパネル(MetaTrader 5)とTelegramアプリ(管理者からのリアルタイムメッセージの受信)

現代の超接続された世界では、数百万もの取引がミリ秒単位で実行されており、取引エコシステムにおいて効率的な通信チャネルを確保することはこれまで以上に重要です。トレーダーの70%以上が、Telegramのようなインスタントメッセージングプラットフォームをリアルタイムのアップデートのために利用しており、取引環境における統合コミュニケーションソリューションの傾向を示しています。このシフトは、トレーダーがEAの洞察と推奨にアルゴリズムで即座にアクセスする必要性を強調しています。

MetaTrader 5を使用するトレーダーや開発者にとっての課題は、自動シグナル生成と距離を超えた効果的な人的介入のギャップを埋めることにあります。管理パネルでは、システム管理者が世界中に分散しているトレーダーと迅速かつ直接コミュニケーションを取ることができます。このシステムを活用することで、管理者はシグナルの有効・無効をシームレスに判断し、Telegramのようなプラットフォームを通じてトレーダーに必要不可欠なコメントや推奨を提供することができます。

この記事では、MQL5を使ったこの重要な管理画面の作成について説明します。本書は、取引コミュニケーションと監視を強化するこのプログラムの開発について、ステップごとのウォークスルーを提供しています。効果的なシグナル管理を促進し、トレーダーとの即時コミュニケーションを統合する包括的なシステムを構築する上でMQL5のパワーを活用する方法を探求し、それによってグローバルな取引業務のあり方に革命をもたらします。

シグナルフローチャート

シグナルフローチャート

フローチャートは、取引エコシステムにおけるシステム、ユーザー、管理者の役割と関連ツールを示しています。

以下は、このディスカッションで取り上げる内容の要約です。

  1. MetaTrader 5 GUI開発における座標について
  2. MQL5開発におけるライブラリファイルについて
  3. MQL5でカスタム管理者パネルアルゴリズムを作成する
  4. トレーダーとのコミュニケーションのためのTelegram APIの統合
  5. 管理画面のテスト
  6. 使い方のヒント
  7. 結論


MetaTrader 5 GUI開発における座標について

MetaTrader 5プラットフォームでグラフィカルユーザーインターフェイス(GUI)を開発する場合、チャート上のグラフィカル要素の位置とサイズを定義する座標系を理解することが不可欠です。

座標系

MetaTrader 5の座標系は、水平軸(x)と垂直軸(y)で定義される2次元空間です。各チャートウィンドウは、原点(0,0)として指定された左上隅から始まる独自の座標系を持ちます。

要素の配置

チャートの座標空間内に要素を配置するには、通常、座標を使って2点を指定します。

  • 左上隅:座標 (x1, y1)で定義された、要素が始まる位置
  • 右下隅:座標(x2, y2)で定義された、要素の対角線上の端

幅と高さの計算

GUI要素の寸法は、これらの座標の差から導き出されます。

  • 幅:幅=x2 - x1として計算されます。
  • 高さ:高さ=y2-y1として計算されます。

これらの計算によって要素のサイズが決定され、チャート上の指定された範囲内に収まるようにします。

パネルレイアウト例

MQL5でのGUIレイアウト例と使用例

実用例

パネルやボタンなどのダイアログやグラフィカルコントロールを作成する場合、これらの座標を使用することで、明確で正確な位置とサイズを設定することができます。このアプローチにより、すべてのGUIコンポーネントが一貫してチャートウィンドウの利用可能なスペースに収まるように配置、拡大縮小され、まとまりのあるユーザーインターフェイスに貢献します。この座標系を理解し、効果的に利用することは、MetaTrader 5環境内で直感的で視覚的に魅力的なアプリケーションを作成するための基本です。

後1つの例は、MetaTrader 5でボタン付きのシンプルなパネルを作成することです。

MetaTrader 5のチャートウィンドウに、先に説明した座標系を使ってボタン付きのシンプルなパネルを作成してみましょう。

座標の定義

まず、パネルとボタンの座標を定義します。チャートの左上隅にパネルを配置し、このパネル内にボタンを配置します。

パネルの座標

  • 左上隅(x1, y1):(10, 10)
  • 右下隅(x2, y2):(200, 100)

これらの座標は、幅190ピクセル(200から10を引いた値)、高さ90ピクセル(100から10を引いた値)で、チャートウィンドウの上から10ピクセル、左から10ピクセルの位置にパネルを作成します。

ボタンの座標

  • 左上隅(x1, y1):(20, 20)
  • 右下隅(x2, y2):(180, 60)

これらの座標は、ボタンをパネル内に配置し、パネルの上から20ピクセル、左から20ピクセル、幅160ピクセル(180から20を引いた値)、高さ40ピクセル(60から20を引いた値)で開始します。少なくとも、数学のことは頭に入ったでしょう。


MQL5開発におけるライブラリファイルについて

MQL5の「.mqh」ファイルはMetaTrader 5プラットフォーム内のコードを整理し、モジュール化するために使用されるヘッダーファイルであり、主に取引アルゴリズムやカスタム指標を開発するために利用されます。このファイルは、複雑なMQL5プロジェクトにおいて、効率的で保守性の高いコード管理をおこなうための重要な要素です。

MetaEditorでは、以下のようにMQL5フォルダにあるインクルードファイルの場所に移動できます。

インクルードファイルの場所

MQL5の#includeファイルを探す

ここでは、その目的と使い方について説明します。

.mqhファイルの目的

1. コードの再利用性:.mqhファイルは、関数定義、クラス宣言、マクロ定義などの複数のMQL5プログラムで共有できる、再利用可能なコードコンポーネントを保持するように設計されています。
2. モジュール性:コードを異なるファイルに分けることで、開発者はモジュール化されたアプリケーションを作ることができます。これにより、特定のコード機能を分離し、維持し、独立して開発することができます。
3. 整理:ヘッダーファイルの使用は、コードを論理的に整理するのに役立ちます。開発者は、ユーティリティ関数や定数を専用のヘッダーに配置するなど、アプリケーションのさまざまな部分をさまざまなファイルに分けておくことができます。

.mqhファイルの典型的な内容

  •  関数宣言:複数のスクリプトにまたがって使用できる関数です。
  •  クラスと構造体:オブジェクト指向プログラミングに使用されるクラスと構造体の定義と実装です。
  •  定数とマクロ:グローバルに使用できる定数値とマクロを定義します。
  •  インクルード:このファイルは他の.mqhファイルを含むこともでき、それによって含まれる機能の階層や連鎖を作ることができます。

.mqhファイルの使い方

MQL5スクリプトで.mqhファイル(.mq5や別の.mqhファイルなど)を使用するには、#includeディレクティブを使用してインクルードします。例を挙げます。
#include <MyLibrary.mqh>

このディレクティブは、#includeディレクティブが表示されている箇所でMyLibrary.mqhの内容をインクルードするようにコンパイラに指示し、スクリプトがインクルードされたヘッダー内で定義された関数、クラス、マクロにアクセスできるようにします。

メリット

  • 読みやすさの向上:複雑なコードをヘッダーに抽象化することで、メインのスクリプトはすっきりとし、理解しやすくなります。
  • メンテナンスの簡素化:1つのヘッダーファイルに変更や更新を加えると、そのヘッダーファイルを含むすべてのスクリプトは、自動的にその更新を継承します。
  • コラボレーション:チーム環境では、コードを複数のファイルに分割することで、異なるメンバーがコードベースの異なる部分を担当し、コンフリクトを避けながら効率的に協力できるようになります。

例えば、このプロジェクトでは、この表にまとめたファイルを実装します。

ファイル名 ファイルの種類 詳細
Dialog.mqh ライブラリファイル
  • このヘッダーファイルには、ダイアログウィンドウの作成と管理、イベント処理、そしてGUIにおけるダイアログの外観や動作をカスタマイズするためのクラス定義とメソッド実装が含まれています。

  • ダイアログを使用することで、より洗練されたユーザーインタラクションを促進でき、ボタンやテキストフィールドなどの様々なコントロールをカプセル化して、チャート上で直接入力を求めたり、情報を提供したりすることが可能です。

Button.mqh ライブラリファイル
  •   ボタンは、ユーザーがアクションを起こしたりデータを送信したりするための基本的なUI要素です。このヘッダーファイルは、通常、ボタンの作成、クリックイベントの処理、ボタンのプロパティや動作をカスタマイズするためのメソッドを提供します。

  • また、ユーザーのインタラクションに反応するイベントリスナーやコールバック関数の実装が含まれている可能性が高く、ユーザーアクションとの機能統合を容易にします。
Edit.mqh  ライブラリファイル
  •   編集コントロールは、テキストボックスや入力フィールドとも呼ばれ、ユーザーがテキストデータを入力または編集できるようにするUI要素です。このヘッダーファイルは、アプリケーションにテキスト入力機能を統合するために必要な構造を提供します。
  • これらの編集コントロールでは、デフォルトのテキスト、フォント、入力検証ルールなどのプロパティを設定でき、ユーザーの入力が正確に受け取られ、適切に処理されるようにします。


MQL5でカスタム管理者パネルアルゴリズムを作成する

MetaEditorで新しいEAテンプレートを作成し、名前を「Admin Panel」または他のユニークな名前にします。開発者プロパティ(名前、バージョン、著作権)だけを残し、残りのテンプレートを消去します。その後、以下のガイドに従ってパネルを組み立てます。

MetaEditorでEAを新規作成する

MetaEditorでEAを新規作成する

Admin Panel.mq5プログラムを構築するにあたっては、すべての要素が特定の目的を果たすと同時に、まとまりのある流れを維持するようにしました。このプログラムの基礎は、Trade.mqh、Dialog.mqh、Button.mqh、Edit.mqhなどの主要なライブラリを含むことから始まります。これらのライブラリは、インタラクティブなチャートベースのインターフェイスを作成するために不可欠なクラスと関数を提供します。これらを取り入れることで、既存のリソースを活用し、カスタム機能に集中することができます。

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

次に、チャート内でスケーラブルかつドラッグ可能なパネルを管理するために CScalableDialog クラスを定義しました。このクラスは CDialog を継承しており、パネルの柔軟な基盤となります。この設計では、ユーザーが簡単にパネルを動かせるようにHandleDraggingメソッドを実装し、動的なリサイズができるようにSetSizeメソッドも追加しました。これにより、さまざまな画面サイズやユーザーの好みに応じた、ユーザーフレンドリーで汎用性の高いインターフェイスを作成することができました。

class CScalableDialog : public CDialog
{
protected:
    bool m_Dragging;
    int m_OffsetX, m_OffsetY;
    int m_width, m_height;

public:
    CScalableDialog() : m_Dragging(false), m_OffsetX(1020), m_OffsetY(720), m_width(500), m_height(400) {}

    void HandleDragging(const int id, const long lparam, const double dparam)
    {
        if (id == CHARTEVENT_MOUSE_MOVE && m_Dragging)
        {
            int new_x = (int)lparam - m_OffsetX;
            int new_y = (int)dparam - m_OffsetY;
            SetXPosition(new_x, new_y);
        }
        else if (id == CHARTEVENT_OBJECT_CLICK)
        {
            m_OffsetX = (int)lparam;
            m_OffsetY = (int)dparam;
            m_Dragging = true;
        }
        else if (id == CHARTEVENT_CLICK)
        {
            m_Dragging = false;
        }
    }

    void SetSize(int width, int height)
    {
        m_width = width;
        m_height = height;
        UpdateDialogSize();
    }

    void UpdateDialogSize()
    {
        ObjectSetInteger(ChartID(), Name(), OBJPROP_XSIZE, m_width);
        ObjectSetInteger(ChartID(), Name(), OBJPROP_YSIZE, m_height);
    }

    void SetXPosition(int x, int y)
    {
        ObjectSetInteger(ChartID(), Name(), OBJPROP_XDISTANCE, x);
        ObjectSetInteger(ChartID(), Name(), OBJPROP_YDISTANCE, y);
        UpdateDialogSize();
    }
};

OnInit 関数では、インターフェイスコンポーネントの初期化に重点を置きました。これには、ボタンや入力ボックスの作成、パネルレイアウトの設定などが含まれます。各要素が正しく配置され、機能的であることに細心の注意を払いました。adminPanel.Createメソッドはメインダイアログを確立し、その後の行ではsendButtonやquickMessageButton、さらにはパネルを最小化または閉じるためのユーティリティボタンなどが追加されます。すべてのコンポーネントがパネル内でスムーズに相互作用できるように配慮しています。

int OnInit()
{
    long chart_id = ChartID();

    if (!adminPanel.Create(chart_id, "Admin Panel", 0, 30, 30, 500, 400))
    {
        Print("Failed to create dialog");
        return INIT_FAILED;
    }

    if (!inputBox.Create(chart_id, "InputBox", 0, 5, 5, 460,50 ))
    {
        Print("Failed to create input box");
        return INIT_FAILED;
    }
    adminPanel.Add(inputBox);

    if (!sendButton.Create(chart_id, "SendButton", 0, 270, 50, 460, 80))
    {
        Print("Failed to create send button");
        return INIT_FAILED;
    }
    sendButton.Text("Send Message");
    adminPanel.Add(sendButton);

    if (!quickMessageButton.Create(chart_id, "QuickMessageButton", 0, 180, 200, 350, 230))
    {
        Print("Failed to create quick message button");
        return INIT_FAILED;
    }
    quickMessageButton.Text("Invalid Signal");
    adminPanel.Add(quickMessageButton);

    if (!minimizeButton.Create(chart_id, "MinimizeButton",0, 405, -22, 435, 0))
    {
        Print("Failed to create minimize button");
        return INIT_FAILED;
    }
    minimizeButton.Text("_");
    adminPanel.Add(minimizeButton);

    if (!closeButton.Create(chart_id, "CloseButton",0 , +435, -22,+465,0))
    {
        Print("Failed to create close button");
        return INIT_FAILED;
    }
    closeButton.Text("X");
    adminPanel.Add(closeButton);

    adminPanel.Show();
    ChartSetInteger(ChartID(), CHART_EVENT_OBJECT_CREATE, true);
    ChartSetInteger(ChartID(), CHART_EVENT_OBJECT_DELETE, true);
    ChartSetInteger(ChartID(), CHART_EVENT_MOUSE_WHEEL, true);
    ChartRedraw();

    Print("Initialization complete");
    return INIT_SUCCEEDED;
}

OnChartEvent関数は、インタラクションロジックを管理するところです。この関数は、クリック、マウス操作、オブジェクト生成イベントなど、さまざまなユーザーアクションを処理します。HandleDraggingのようなメソッドを呼び出すことで、パネルがユーザーの入力に動的に反応できるようになります。OnChartEvent内のスイッチケース構造により、さまざまなイベントをそれぞれのハンドラに効率的にルーティングすることができ、インターフェイスの応答性と直感性を維持することができます。

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

    switch(id)
    {
        case CHARTEVENT_KEYDOWN:
            Print("Key pressed: code=", lparam);
            break;
        
        case CHARTEVENT_MOUSE_MOVE:
            Print("Mouse move: x=", lparam, "  y=", dparam);
            break;

        case CHARTEVENT_OBJECT_CREATE:
            Print("Created object: ", sparam);
            break;

        case CHARTEVENT_OBJECT_DELETE:
            Print("Deleted object: ", sparam);
            break;

        case CHARTEVENT_CLICK:
            Print("Mouse click on chart: x=", lparam, "  y=", dparam);
            break;

        case CHARTEVENT_OBJECT_CLICK:
            if (sparam == "SendButton")
            {
                OnSendButtonClick();
            }
            else if (sparam == "QuickMessageButton")
            {
                OnQuickMessageButtonClick();
            }
            else if (sparam == "MinimizeButton")
            {
                OnMinimizeButtonClick();
            }
            else if (sparam == "CloseButton")
            {
                OnCloseButtonClick();
            }
            break;
    }
}

最後に、ボタンのクリックを処理する関数(OnSendButtonClick、OnQuickMessageButtonClick、OnMinimizeButtonClick、OnCloseButtonClick)があり、ここでプログラムはユーザー入力に基づいて特定のアクションを実行します。例えば、OnSendButtonClickは、入力ボックスからメッセージを取得し、それをTelegramに送信し、操作の成功をフィードバックします。これらの関数は、ユーザーのアクションを意味のある結果に変換するため、簡単でありながら非常に重要です。ここでは、インターフェイスが機能的であるだけでなく、ユーザーの期待に沿うものであることを確認しました。
void OnSendButtonClick()
{
    string customMessage = inputBox.Text();
    if (SendMessageToTelegram(customMessage))
    {
        Print("Message sent: ", customMessage);
    }
    else
    {
        Print("Failed to send message.");
    }
}

void OnQuickMessageButtonClick()
{
    if (SendMessageToTelegram(QuickMessage))
    {
        Print("Quick message sent: ", QuickMessage);
    }
    else
    {
        Print("Failed to send quick message.");
    }
}

void OnMinimizeButtonClick()
{
    static bool minimized = false;
    if (minimized)
    {
        adminPanel.SetSize(500, 400);
        minimized = false;
    }
    else
    {
        adminPanel.SetSize(500, 30);
        minimized = true;
    }
}

void OnCloseButtonClick()
{
    adminPanel.Destroy();
    Print("Panel closed.");
}


トレーダーとのコミュニケーションのためのTelegram APIの統合

MQL5プログラムにTelegramメッセージングを統合するにあたり、取引インターフェイスとTelegramチャット間の通信を容易にするため、SendMessageToTelegram関数を作成しました。この関数は、まずbotTokenとchatIdという2つの重要な変数を定義します。これらはTelegramが提供する一意の識別子で、ボットを認証し、メッセージを送信するチャットを指定することができます。これらの値をハードコーディングすることで、メッセージが正しい宛先に安全かつ効率的に送信されるようにしています。「ボットトークン」と「チャットID」については、または私の過去の記事をご覧ください。

string botToken = "BOT TOKEN";
string chatId = "CHAT ID";

次に、Telegram Bot APIのエンドポイントである url文字列を構築し、botTokenと組み合わせて、メッセージ送信に必要な完全なURLを作成します。この手順は、Telegramサーバーへの接続を設定し、システムがプログラムでTelegramサーバーとやりとりできるようにするために重要です。これと並行してpost_data char配列を宣言して、メッセージのペイロードを保持します。これは、APIの要件を満たすために JSON 形式で形式されます。

string url = "https://api.telegram.org/bot" + botToken + "/sendMessage";
char post_data[];

メッセージをJSONで形式するために、chatIdと実際のmessageテキストをjsonMessage文字列に連結します。その後、この文字列はStringToCharArrayを使って文字配列に変換され、ArrayResizeを使って配列のサイズが変更されます。この手順では、メッセージ構造がTelegram APIの期待値と互換性があることを確認します。これはコミュニケーションを成功させるために不可欠です。

string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}";
ArrayResize(post_data, StringToCharArray(jsonMessage, post_data));

この関数は、次にtimeout変数を設定し、プログラムがTelegramサーバーからの応答を待つ時間を定義します。サーバーのレスポンスを格納するresult配列と、返された追加情報を格納するresponseHeaders文字列を用意します。これらの変数は、応答を処理し、リクエスト中に発生する可能性のある問題を診断するために重要です。

int timeout = 5000;
char result[];
string responseHeaders;

WebRequest関数では、Telegram APIとの実際の通信がおこなわれます。URLにPOSTリクエストを送り、必要なヘッダー、タイムアウト、post_dataを渡します。リクエストが成功し、HTTP 200レスポンスが返ってきた場合、この関数は成功メッセージを表示し、trueを返します。そうでなければ、HTTPステータスコードとエラーメッセージを表示し、falseを返します。このエラー処理は、デバッグやメッセージ送信機能の信頼性を確保するために不可欠です。

int res = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, post_data, result, responseHeaders);

if (res == 200) // HTTP 200 OK
{
    Print("Message sent successfully: ", message);
    return true;
}
else
{
    Print("Failed to send message. HTTP code: ", res, " Error code: ", GetLastError());
    Print("Response: ", CharArrayToString(result));
    return false;
}

すべてのスニペットを組み合わせた最終的なコードはこうなります。

//+------------------------------------------------------------------+
//|                                          Admin Panel.mq5         |
//|                   Copyright 2024, Clemence Benjamin              |
//|       https://www.mql5.com/ja/users/billionaire2024/seller       |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/ja/users/billionaire2024/seller"
#property description "A responsive Admin Panel. Send messages to your telegram clients without leaving MT5"
#property version   "1.06"

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

//+------------------------------------------------------------------+
//| Creating a scalable and draggable panel                          |
//+------------------------------------------------------------------+
class CScalableDialog : public CDialog
{
protected:
    bool m_Dragging;
    int m_OffsetX, m_OffsetY;
    int m_width, m_height;

public:
    CScalableDialog() : m_Dragging(false), m_OffsetX(1020), m_OffsetY(720), m_width(500), m_height(400) {}

    // Handle the event to allow dragging
    void HandleDragging(const int id, const long lparam, const double dparam)
    {
        if (id == CHARTEVENT_MOUSE_MOVE && m_Dragging)
        {
            int new_x = (int)lparam - m_OffsetX;
            int new_y = (int)dparam - m_OffsetY;
            SetXPosition(new_x, new_y); // Update the position without changing size
        }
        else if (id == CHARTEVENT_OBJECT_CLICK)
        {
            m_OffsetX = (int)lparam;
            m_OffsetY = (int)dparam;
            m_Dragging = true;
        }
        else if (id == CHARTEVENT_CLICK)
        {
            m_Dragging = false;
        }
    }

    void SetSize(int width, int height)
{
    m_width = width;
    m_height = height;

    // Call the method to update the size of the dialog
    UpdateDialogSize();
}

void UpdateDialogSize()
{
    // Adjust the internal layout or size of the controls within the dialog here
    // Example: Resize or reposition child controls based on the new dimensions

    // Ensure that dialog dimensions are respected within its design
    ObjectSetInteger(ChartID(), Name(), OBJPROP_CORNER, 0); // This aligns the dialog to the chart corner
    ObjectSetInteger(ChartID(), Name(), OBJPROP_XSIZE, m_width);  // Width of the dialog
    ObjectSetInteger(ChartID(), Name(), OBJPROP_YSIZE, m_height); // Height of the dialog
}


  void SetXPosition(int x, int y)
{
    // Set the X and Y positions of the dialog panel
    ObjectSetInteger(ChartID(), Name(), OBJPROP_XDISTANCE, x);
    ObjectSetInteger(ChartID(), Name(), OBJPROP_YDISTANCE, y);

    // Call the method to update the size of the dialog
    UpdateDialogSize();
}


};

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input string QuickMessage = "Invalid Signal"; // Default quick message

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
CScalableDialog adminPanel;
CButton sendButton;
CButton quickMessageButton;
CButton minimizeButton;
CButton closeButton;
CEdit inputBox;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    long chart_id = ChartID();

    // Create the dialog
    if (!adminPanel.Create(chart_id, "Admin Panel", 0, 30, 30, 500, 400))
    {
        Print("Failed to create dialog");
        return INIT_FAILED;
    }

    // Create the input box
    if (!inputBox.Create(chart_id, "InputBox", 0, 5, 5, 460,50 ))
    {
        Print("Failed to create input box");
        return INIT_FAILED;
    }
    adminPanel.Add(inputBox);

    // Create the send button for custom messages
    if (!sendButton.Create(chart_id, "SendButton", 0, 270, 50, 460, 80))
    {
        Print("Failed to create send button");
        return INIT_FAILED;
    }
    sendButton.Text("Send Message");
    adminPanel.Add(sendButton);

    // Create the quick message button
    if (!quickMessageButton.Create(chart_id, "QuickMessageButton", 0, 180, 200, 350, 230))
    {
        Print("Failed to create quick message button");
        return INIT_FAILED;
    }
    quickMessageButton.Text("Invalid Signal");
    adminPanel.Add(quickMessageButton);

    // Create the minimize button
    if (!minimizeButton.Create(chart_id, "MinimizeButton",0, 405, -22, 435, 0))
    {
        Print("Failed to create minimize button");
        return INIT_FAILED;
    }
    minimizeButton.Text("_");
    adminPanel.Add(minimizeButton);

    // Create the close button
    if (!closeButton.Create(chart_id, "CloseButton",0 , +435, -22,+465,0))
    {
        Print("Failed to create close button");
        return INIT_FAILED;
    }
    closeButton.Text("X");
    adminPanel.Add(closeButton);

    adminPanel.Show();

    // Enable chart events
    ChartSetInteger(ChartID(), CHART_EVENT_OBJECT_CREATE, true);
    ChartSetInteger(ChartID(), CHART_EVENT_OBJECT_DELETE, true);
    ChartSetInteger(ChartID(), CHART_EVENT_MOUSE_WHEEL, true);
    ChartRedraw();

    Print("Initialization complete");
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    adminPanel.Destroy();
    Print("Deinitialization complete");
}

//+------------------------------------------------------------------+
//| Expert event handling function                                   |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    // Handle dragging and other events
    adminPanel.HandleDragging(id, lparam, dparam);

    // Handle different types of events
    switch(id)
    {
        case CHARTEVENT_KEYDOWN:
            Print("Key pressed: code=", lparam);
            break;
        
        case CHARTEVENT_MOUSE_MOVE:
            Print("Mouse move: x=", lparam, "  y=", dparam);
            break;

        case CHARTEVENT_OBJECT_CREATE:
            Print("Created object: ", sparam);
            break;

        case CHARTEVENT_OBJECT_DELETE:
            Print("Deleted object: ", sparam);
            break;

        case CHARTEVENT_CLICK:
            Print("Mouse click on chart: x=", lparam, "  y=", dparam);
            break;

        case CHARTEVENT_OBJECT_CLICK:
            if (sparam == "SendButton")
            {
                OnSendButtonClick();
            }
            else if (sparam == "QuickMessageButton")
            {
                OnQuickMessageButtonClick();
            }
            else if (sparam == "MinimizeButton")
            {
                OnMinimizeButtonClick();
            }
            else if (sparam == "CloseButton")
            {
                OnCloseButtonClick();
            }
            break;

        default:
            if (id > CHARTEVENT_CUSTOM)
                Print("Custom event ID=", id, " lparam=", lparam, " dparam=", dparam, " sparam=", sparam);
            break;
    }
}

//+------------------------------------------------------------------+
//| Function to handle custom message send button click              |
//+------------------------------------------------------------------+
void OnSendButtonClick()
{
    string message = inputBox.Text();
    if (message != "")
    {
        if(SendMessageToTelegram(message))
            Print("Custom message sent: ", message);
        else
            Print("Failed to send custom message.");
    }
    else
    {
        Print("No message entered.");
    }
}

//+------------------------------------------------------------------+
//| Function to handle quick message button click                    |
//+------------------------------------------------------------------+
void OnQuickMessageButtonClick()
{
    if(SendMessageToTelegram(QuickMessage))
        Print("Quick Message Button Clicked - Quick message sent: ", QuickMessage);
    else
        Print("Failed to send quick message.");
}

//+------------------------------------------------------------------+
//| Function to handle minimize button click                         |
//+------------------------------------------------------------------+
void OnMinimizeButtonClick()
{
    static bool minimized = false;
    if (minimized)
    {
        // Restore full size
        adminPanel.SetSize(500, 400);  // Restore full size (400x200)
    }
    else
    {
        // Minimize to header only
        adminPanel.SetSize(100, 80);   // Minimize height to 30 (keeping the width 400)
    }
    minimized = !minimized;
}

//+------------------------------------------------------------------+
//| Function to handle close button click                            |
//+------------------------------------------------------------------+
void OnCloseButtonClick()
{
    adminPanel.Destroy();
    Print("Admin Panel closed.");
}

///+------------------------------------------------------------------+
//| Function to send the message to Telegram                         |
//+------------------------------------------------------------------+
bool SendMessageToTelegram(string message)
{
    // Replace with your bot token and chat ID
    string botToken = "Your BOT TOKEN";
    string chatId = "Your Chat ID";

    string url = "https://api.telegram.org/bot" + botToken + "/sendMessage";
    char post_data[];

    // Prepare the message data
    string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}";

    // Resize the character array to fit the JSON payload
    ArrayResize(post_data, StringToCharArray(jsonMessage, post_data));

    int timeout = 5000;
    char result[];
    string responseHeaders;

    // Make the WebRequest
    int res = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, post_data, result, responseHeaders);

    if (res == 200) // HTTP 200 OK
    {
        Print("Message sent successfully: ", message);
        return true;
    }
    else
    {
        Print("Failed to send message. HTTP code: ", res, " Error code: ", GetLastError());
        Print("Response: ", CharArrayToString(result));
        return false;
    }
}


管理画面のテスト

コンパイルに成功した後、プログラムを起動すると、意図したとおりに動作しました。

EAから管理パネルを起動する

EAから管理パネルを起動する: Boom 500指数


使い方のヒント

Telegramボットとチャンネルを作成し、TelegramボットAPIにアクセスできる場合は、以下のソースコードを編集して、ボットトークンとチャットIDをハードコードするだけで、管理パネルを使用できるようになります。あるいは、トークンを入手して、以下の添付ファイルで提供されるコンパイル済みEAに入力するだけで、ソースコードを変更する必要はありません。

ボットトークンとチャットIDを入力

ボットトークンとチャットIDの入力


結論

MQL5における管理者パネルEAの開発は、MetaTrader 5プラットフォームを通じてトレーダーを効果的に管理し、コミュニケーションを取る上での重要な進展を示しています。ダイナミックでスケーラブルなGUIとTelegramを通じたリアルタイムのメッセージング機能を統合することで、このツールは取引業務の効率と応答性を向上させています。パネルから直接クイックメッセージやカスタム通知を送信できるため、即時のコミュニケーションが実現し、重要な情報が迅速に伝達されます。このプロジェクトは、ユーザーフレンドリーなインターフェイスと堅牢なコミュニケーションツールを組み合わせることの可能性を強調し、よりインタラクティブで効果的な取引管理ソリューションへの道を開くものです。

特に最近の市場動向を考慮すると、メッセージを入力してから送信するまでの時間を短縮するための他のクイックボタンの追加など、さらなる開発が不可欠です。この点については、今後の連載で詳しく掘り下げていきます。トレーダーの皆さんには感謝しています。開発をお楽しみください。

ご意見はいつでも大歓迎です。プロジェクトで添付ファイルを活用してみてください。

 ファイル名 ファイルの説明
Admin Panel.mq5 管理パネルのソースコード
Admin Panel.ex5 管理パネルEA。正しいボットトークンとチャットIDの入力が必要
 (terminal64_bK0FbSvNmw)  管理パネルの動作画像

トップに戻る

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

添付されたファイル |
Admin_Panel.mq5 (21.75 KB)
Admin_Panel.ex5 (121.76 KB)
最後のコメント | ディスカッションに移動 (3)
Cracker Delicious
Cracker Delicious | 22 8月 2024 において 02:39
ありがとう
Clemence Benjamin
Clemence Benjamin | 23 8月 2024 において 10:09
Cracker Delicious #:
ありがとう

ウェルカム!クラッカー・デリシャス

Clemence Benjamin
Clemence Benjamin | 23 8月 2024 において 13:43

MetaTrader 5 で、ファイルに添付されているコンパイルされた Admin Panel を使用する場合は、Expert Advisor の設定で Ctrl + O を押して WebRequest を有効にしてください。また、Telegram API へのリンクを追加してください。

WebRequestを許可する



Candlestick Trend Constraintモデルの構築(第8回):エキスパートアドバイザー(EA)の開発 (II) Candlestick Trend Constraintモデルの構築(第8回):エキスパートアドバイザー(EA)の開発 (II)
独立したEAについて考えてみましょう。前回は、リスクとリターンのジオメトリを描くために独立したスクリプトと連携する、指標ベースのEAについて説明しました。今回は、すべての機能を1つのプログラムに統合したMQL5 EAのアーキテクチャについて解説します。
知っておくべきMQL5ウィザードのテクニック(第33回):ガウス過程カーネル 知っておくべきMQL5ウィザードのテクニック(第33回):ガウス過程カーネル
ガウス過程カーネルは正規分布の共分散関数であり、予測において役割を果たす可能性があります。MQL5のカスタムシグナルクラスで、このユニークなアルゴリズムを探求し、プライムエントリシグナルやエグジットシグナルとして活用できるかを検証しました。
ダイナミックマルチペアEAの形成(第1回):通貨相関と逆相関 ダイナミックマルチペアEAの形成(第1回):通貨相関と逆相関
ダイナミックマルチペアEAは、相関戦略と逆相関戦略の両方を活用し、取引パフォーマンスの最適化を図ります。リアルタイムの市場データを分析することで、通貨ペア間の相関関係や逆相関関係を特定し、それらを取引に活かします。
MQL5とデータ処理パッケージの統合(第2回):機械学習と予測分析 MQL5とデータ処理パッケージの統合(第2回):機械学習と予測分析
本連載では、MQL5とデータ処理パッケージの統合について考察し、機械学習と予測分析の強力な組み合わせを深掘りします。MQL5と一般的な機械学習ライブラリをシームレスに接続することで、金融市場向けの高度な予測モデルを実現する方法を探ります。