English Deutsch
preview
プライスアクション分析ツールキットの開発(第17回):TrendLoom EAツール

プライスアクション分析ツールキットの開発(第17回):TrendLoom EAツール

MetaTrader 5トレーディングシステム |
89 3
Christian Benjamin
Christian Benjamin

内容



はじめに

トレンドアナリストによって、市場分析やエントリーの確認方法はさまざまです。多くのトレーダーは、M1・M5・M15や、H1・H4・W1といった複数の時間枠を確認し、エントリーの妥当性を判断し、シグナルの信頼性を高めています。しかし、時間枠をいちいち切り替えずに、ボタンを1回押すだけでリアルタイムに更新された情報が表示され、自動的に最新のトレンドが反映される方法があったら便利だと思いませんか。短い時間枠で売りが出ていたために取引を始め、その後、長い時間枠のチャートを見て実は買いトレンドだったと気付いたことはありませんか。

TrendLoom EAは、そういった間違いを防ぐために設計されています。このツールには、7つのボタンからなるパネルが搭載されており、それぞれ異なる取引スタイルに対応しています。各ボタンは3つの時間枠を同時に分析し、移動平均線を使ってBUY(買い)、SELL(売り)、NEUTRAL(中立)のようなシグナルを表示します。この強力なツールはシグナルを検出するたびに迅速に更新され、常に関連する情報を表示します。


戦略の概要

TrendLoom EAは、パネル形式のグラフィカルインターフェイスで構成されています。パネルには、特定の取引戦略に対応する7つのボタンがあります。
  • 短期フォーカス(M1・M5・M15)
  • スキャルピング/デイトレード(M5・M15・H1)
  • スイングトレード(M15・H1・H4)
  • トレンドトレード(H1・H4・D1)
  • MTFトレンド確認(H1・H4・W1)
  • ショートスキャルパー/中期トレンド(M5・H1・D1)
  • 長期トレンド(H1・D1・W1)

以下は、ボタンが押されたときにEAがBUY、SELL、NEUTRALのシグナルを生成する流れです。

  • データ収集:選択された3つの時間枠(例:M1、M5、M15)について、それぞれ直近で確定したローソク足の終値を取得します。
  • SMAの計算: 各時間枠ごとに、50期間のSMA(Simple Moving Average、単純移動平均)を算出します。SMAは現在価格の基準として使用されます。
個別シグナルの生成:終値とSMAを比較し、以下のように数値シグナルを割り当てます。

  • 終値がSMAより上 → +1(強気)
  • 終値がSMAより下 → -1(弱気)
  • 終値がSMAと等しい → 0(中立)

シグナルの統合

  • 3つの時間枠から得られた個別シグナルを合計します。
  • 最終シグナルの決定:
  • 合計が+2以上 → 強い上昇モメンタム → BUY(買い)
  • 合計が-2以下 → 強い下降モメンタム → SELL(売り)
  • 上記以外 → シグナルは混在または中立 → NEUTRAL(中立)

プロセスをよりよく理解するために、次の図を確認してみましょう。

フロー図

図1:フローチャート


MQL5での実装

ファイルの最上部には、ヘッダーコメントとEAのプロパティ定義が記述されています。これらの行はEAのメタデータとして機能し、著作権情報、バージョン、ソースとの関連付けを示しています。#property strictディレクティブは、より厳格なコンパイルルールを適用するために使用され、一般的なコーディングミスを防ぐのに役立ちます。

//+------------------------------------------------------------------+
//|                                                 TrendLoom EA.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/ja/users/lynnchris"
#property version   "1.00"
#property strict

次に、コードには複数のヘッダーファイルが含まれており、ダイアログ、ボタン、ラベル、パネルなどのコントロールを提供しています。また、チャート上にテキストを表示するためのチャートオブジェクトコントロールも取り込まれています。このようにモジュール形式でファイルを読み込むことで、EAはユーザーインターフェイス構築のために、あらかじめ用意されたクラスを利用することができます。

これらの#includeディレクティブは、MetaEditorの「Include」フォルダ内のライブラリを参照しています。Controlsサブフォルダにあるファイルは、ダイアログやボタン用の組み込みクラスを提供し、ラベルやパネル用のクラスも含まれています。これにより、コードを一から書き直すことなく、対話型インターフェイスの構築が簡単になります。また、ChartObjectsサブフォルダにあるChartObjectsTxtControls.mqhファイルは、チャート上に動的なテキストを表示するための機能を提供します。

#include <Controls/Dialog.mqh>
#include <Controls/Button.mqh>
#include <Controls/Label.mqh>
#include <Controls/Panel.mqh>
#include <ChartObjects/ChartObjectsTxtControls.mqh>  // Adjusted include path (singular folder)

次に、テキストの配置と色の値の定数を定義します。これにより、コードの明瞭性と保守性が向上します。

#ifndef ALIGN_LEFT
  #define ALIGN_LEFT   0
#endif
#ifndef ALIGN_CENTER
  #define ALIGN_CENTER 1
#endif
#ifndef ALIGN_RIGHT
  #define ALIGN_RIGHT  2
#endif

#define clrSilver 0xC0C0C0

EAでは、パネルおよびそのボタンの外観や位置を調整するための入力パラメータが宣言されています。PanelX、PanelY、PanelWidthはパネルの配置やサイズを設定します。カラー関連の入力は、パネル全体のビジュアルテーマを決定します。ボタンのサイズや間隔は、btnWidth、btnHeight、btnSpacingによって制御されており、レイアウトや配色を自由にカスタマイズできるようになっています。このような構成により、ユーザーインターフェイスを自分の好みに合わせて柔軟に調整することが可能です。

//---- Input parameters -----------------------------------------------
input int    PanelX               = 10;
input int    PanelY               = 10;
input int    PanelWidth           = 250;
input int    btnWidth             = 220;
input int    btnHeight            = 30;
input int    btnSpacing           = 5;

input color  PanelBackgroundColor = clrDimGray;
input color  PanelHeaderColor     = clrBlueViolet;
input color  ButtonBgColor        = clrBlack;
input color  ButtonTextColor      = clrBlueViolet;
input color  AnalysisTextColor    = clrLime;

ボタンの名前や表示テキストは配列に格納されており、新しいボタンの追加や既存ボタンの更新を素早く簡単におこなうことができます。この設計により、ボタンに関する情報が一箇所に集中管理されるため、変更が必要な場合でも最小限の修正で済みます。また、ユーザーインターフェイス全体の一貫性が保たれ、エラーの発生リスクも軽減されます。この手法は、将来的な機能拡張にも柔軟に対応でき、コードの整理や保守性の向上にも寄与します。

//---- Button Names and Texts (7 analysis options) --------------------
string buttonNames[7] =
  {
   "btnShortTerm",
   "btnScalping",
   "btnSwing",
   "btnTrend",
   "btnMTFTrend",
   "btnShortScalper",
   "btnLongTerm"
  };

string buttonTexts[7] =
  {
   "Short Term Focus\n(M1, M5, M15)",
   "Scalping/Intraday\n(M5, M15, H1)",
   "Swing Trading\n(M15, H1, H4)",
   "Trend Trading\n(H1, H4, D1)",
   "MTF Trend Confirm\n(H1, H4, W1)",
   "Short Scalper/Mid Trend\n(M5, H1, D1)",
   "Long Term Trend\n(H1, D1, W1)"
  };

グローバルマクロでは、パネルのヘッダー名や分析ラベル名が定義されています。これらのマクロは、コード全体での一貫性を保つ役割を果たし、これらの識別子の唯一の参照元として機能します。名前を一箇所に集約することで、パネルの各コンポーネントを更新する際も簡単になり、タイプミスのリスクも減少します。 この手法により、保守性が向上し、コード全体がシンプルかつ明確になります。

// Global object names for panel header and analysis label
#define PANEL_BG "PanelBG"
#define PANEL_HEADER "PanelHeader"
#define ANALYSIS_LABEL "AnalysisResult"

コードは次に、2つの補助関数を宣言しています。GetSMAは単純移動平均(SMA)を計算し、AnalyzeTimeframesは複数の時間枠にわたる市場分析を実行します。これらの関数は、市場分析の中核となるロジックを構成しています。

//--- Helper function declarations
double GetSMA(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift);
string AnalyzeTimeframes(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2, ENUM_TIMEFRAMES tf3);

カスタムクラスCTrendLoomPanelCAppDialogを継承しています。このクラスは、ヘッダーラベル、メインパネル、ボタン、結果ラベルなど、すべてのユーザーインターフェイス要素をまとめています。この設計により、モジュール化されたインターフェイスが構築され、管理や拡張が容易になります。

パネルの作成

CreateTrendPanelメソッドは、まずダイアログウィンドウを作成します。その後、カスタムテキスト、色、フォントサイズ、フォントスタイルを設定したヘッダーラベルを準備します。配置はObjectSetInteger関数を使って設定されます。

bool CreateTrendPanel(const long chart, const string name, const int x1, const int y1, const int x2, const int y2)
{
   if(!CAppDialog::Create(chart, name, 0, x1, y1, x2, y2))
   {
      Print("Failed to create TrendLoom dialog.");
      return false;
   }
   if(!m_lblHeader.Create(0, "TrendLoomHeader", 0, 10, 10, x2 - x1 - 20, 30))
   {
      Print("Failed to create header label.");
      return false;
   }
   m_lblHeader.Text("TrendLoom EA");
   m_lblHeader.Color(PanelHeaderColor);
   m_lblHeader.FontSize(14);
   m_lblHeader.Font("Segoe UI");
   Add(m_lblHeader);
   if(!ObjectSetInteger(0L, m_lblHeader.Name(), OBJPROP_ALIGN, (long)ALIGN_CENTER))
      Print("Failed to set header alignment");

メソッドは続けてメインパネルを作成し、そのサイズを動的に計算します。次に、それぞれのボタンを作成し、順番に配置していきます。最後に、ボタンの下に分析結果を表示するための結果ラベルが追加されます。

イベントの処理

OnEventメソッドはユーザーの操作を処理します。ボタンがクリックされると、対応する時間枠のパラメータを使ってAnalyzeTimeframesを呼び出します。分析結果はパネルに表示され、アラートも表示されます。

bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   if(sparam == "btnShortTerm")
   {
      string res = AnalyzeTimeframes(PERIOD_M1, PERIOD_M5, PERIOD_M15);
      string out = "Short Term Focus: " + res;
      UpdateResults(out);
      Alert(out);
      return true;
   }
   else if(sparam == "btnScalping")
   {
      string res = AnalyzeTimeframes(PERIOD_M5, PERIOD_M15, PERIOD_H1);
      string out = "Scalping/Intraday: " + res;
      UpdateResults(out);
      Alert(out);
      return true;
   }
   // Additional conditions for other buttons
   return false;
}

UIの更新

UpdateResultsメソッドは、新しい分析データを用いて、結果ラベルを更新します。その後、ChartRedrawを呼び出して、更新された情報を即座にチャートに反映させます。

void UpdateResults(const string &result)
{
   m_lblResults.Text("Analysis Result: " + result);
   ChartRedraw();
}

コア分析関数

単純移動平均(SMA)の計算

GetSMA関数は、iMA関数を使ってインジケーターハンドルを作成し、SMAを計算します。インジケーターバッファからSMAの値をコピーし、その後ハンドルを解放してリソースを開放します。
double GetSMA(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift)
{
   int handle = iMA(symbol, timeframe, period, 0, MODE_SMA, PRICE_CLOSE);
   if(handle == INVALID_HANDLE)
   {
      Print("Failed to create iMA handle for timeframe ", timeframe);
      return 0.0;
   }
   double sma[];
   if(CopyBuffer(handle, 0, shift, 1, sma) <= 0)
   {
      Print("Failed to copy buffer for timeframe ", timeframe);
      IndicatorRelease(handle);
      return 0.0;
   }
   double result = sma[0];
   IndicatorRelease(handle);
   return result;
}

多時間枠の分析

AnalyzeTimeframes関数は、3つの時間枠における終値とSMAを取得し、終値がSMAを上回る場合は強気シグナル、下回る場合は弱気シグナルを割り当てます。これらの個別シグナルを合計し、合計が2以上ならBUY、-2以下ならSELL、それ以外はNEUTRALの最終推奨シグナルを生成します。各時間枠は独立して評価され、市場トレンドのバランスの取れた見方を実現しています。また、shiftパラメータにより、分析対象は最後に確定したローソク足のみに限定されます。複数の時間枠のシグナルを組み合わせることで、一時的な市場ノイズの影響を軽減し、SMAの期間を調整することで、取引シグナルの感度をさらに最適化することが可能です。

string AnalyzeTimeframes(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2, ENUM_TIMEFRAMES tf3)
{
   int period = 50;
   int shift  = 1; // last completed candle

   double price1 = iClose(_Symbol, tf1, shift);
   double sma1   = GetSMA(_Symbol, tf1, period, shift);
   int signal1   = (price1 > sma1) ? 1 : (price1 < sma1 ? -1 : 0);

   double price2 = iClose(_Symbol, tf2, shift);
   double sma2   = GetSMA(_Symbol, tf2, period, shift);
   int signal2   = (price2 > sma2) ? 1 : (price2 < sma2 ? -1 : 0);

   double price3 = iClose(_Symbol, tf3, shift);
   double sma3   = GetSMA(_Symbol, tf3, period, shift);
   int signal3   = (price3 > sma3) ? 1 : (price3 < sma3 ? -1 : 0);

   int sum = signal1 + signal2 + signal3;
   if(sum >= 2)
      return "BUY";
   else if(sum <= -2)
      return "SELL";
   else
      return "NEUTRAL";
}
EAのライフサイクル関数は、初期化、クリーンアップ、イベント処理を担当します。OnInit関数は、入力パラメータを使ってTrendLoomパネルを作成します。パネルの作成に失敗した場合、EAは初期化エラーを返します。
int OnInit()
{
   if(!TrendPanel.CreateTrendPanel(0, "TrendLoom Panel", PanelX, PanelY, PanelX + PanelWidth + 20, PanelY + 400))
   {
      Print("Failed to create TrendLoom Panel.");
      return INIT_FAILED;
   }
   return INIT_SUCCEEDED;
}

OnDeinit関数は、EAが削除されるかチャートが閉じられた際に、パネルを破棄してクリーンアップをおこないます。

void OnDeinit(const int reason)
{
   TrendPanel.Destroy(reason);
}

最後に、OnChartEvent関数はチャートイベントをパネルのイベントハンドラに転送し、インターフェイスの応答性を維持します。

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

MetaEditorでは、必要なファイルは「Include」フォルダに格納されています。コード内で指定されているファイルにアクセスするには、以下の図のようにサブフォルダを参照してください。このフォルダ構成により、コンパイラはダイアログ、ボタン、ラベル、パネルのコントロールファイルを「Include/Controls」フォルダから、チャートオブジェクトのコントロールファイルを「Include/ChartObjects」フォルダから正しく読み込むことができます。

#include <Controls/Dialog.mqh>
#include <Controls/Button.mqh>
#include <Controls/Label.mqh>
#include <Controls/Panel.mqh>
#include <ChartObjects/ChartObjectsTxtControls.mqh>  // Adjusted include path (singular folder)

ステップ1 

図2:手順1

手順2

図3:手順2


MQL5コード

//+------------------------------------------------------------------+
//|                                                 TrendLoom EA.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/ja/users/lynnchris"
#property version   "1.00"
#property strict

#include <Controls/Dialog.mqh>
#include <Controls/Button.mqh>
#include <Controls/Label.mqh>
#include <Controls/Panel.mqh>
#include <ChartObjects/ChartObjectsTxtControls.mqh>  // Adjusted include path (singular folder)

// Define alignment constants if not already defined
#ifndef ALIGN_LEFT
#define ALIGN_LEFT   0
#endif
#ifndef ALIGN_CENTER
#define ALIGN_CENTER 1
#endif
#ifndef ALIGN_RIGHT
#define ALIGN_RIGHT  2
#endif

#define clrSilver 0xC0C0C0

//---- Input parameters -----------------------------------------------
input int    PanelX               = 10;       // Top-left X coordinate of panel
input int    PanelY               = 10;       // Top-left Y coordinate of panel
input int    PanelWidth           = 250;      // Panel width (for longer text)
input int    btnWidth             = 220;      // Button width
input int    btnHeight            = 30;       // Button height
input int    btnSpacing           = 5;        // Spacing between buttons

input color  PanelBackgroundColor = clrDimGray;     // Panel background color
input color  PanelHeaderColor     = clrBlueViolet;  // Panel header text color

input color  ButtonBgColor        = clrBlack;       // Button background color
input color  ButtonTextColor      = clrBlueViolet;  // Button text color

input color  AnalysisTextColor    = clrLime;        // Analysis result text color

//---- Button Names and Texts (7 analysis options) --------------------
string buttonNames[7] =
  {
   "btnShortTerm",
   "btnScalping",
   "btnSwing",
   "btnTrend",
   "btnMTFTrend",
   "btnShortScalper",
   "btnLongTerm"
  };

string buttonTexts[7] =
  {
   "Short Term Focus\n(M1, M5, M15)",
   "Scalping/Intraday\n(M5, M15, H1)",
   "Swing Trading\n(M15, H1, H4)",
   "Trend Trading\n(H1, H4, D1)",
   "MTF Trend Confirm\n(H1, H4, W1)",
   "Short Scalper/Mid Trend\n(M5, H1, D1)",
   "Long Term Trend\n(H1, D1, W1)"
  };

// Global object names for panel header and analysis label
#define PANEL_BG "PanelBG"
#define PANEL_HEADER "PanelHeader"
#define ANALYSIS_LABEL "AnalysisResult"

//--- Helper function declarations
double GetSMA(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift);
string AnalyzeTimeframes(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2, ENUM_TIMEFRAMES tf3);

//------------------------------------------------------------------------------
// CTrendLoomPanel class - A modern, modular panel for TrendLoom EA
//------------------------------------------------------------------------------
class CTrendLoomPanel : public CAppDialog
  {
private:
   CLabel            m_lblHeader;
   CPanel            m_panelMain;
   CButton           m_btnShortTerm;
   CButton           m_btnScalping;
   CButton           m_btnSwing;
   CButton           m_btnTrend;
   CButton           m_btnMTFTrend;
   CButton           m_btnShortScalper;
   CButton           m_btnLongTerm;
   CLabel            m_lblResults;

public:
   // Create the TrendLoom Panel dialog
   bool              CreateTrendPanel(const long chart, const string name, const int x1, const int y1, const int x2, const int y2)
     {
      if(!CAppDialog::Create(chart, name, 0, x1, y1, x2, y2))
        {
         Print("Failed to create TrendLoom dialog.");
         return false;
        }
      // Create header label
      if(!m_lblHeader.Create(0, "TrendLoomHeader", 0, 10, 10, x2 - x1 - 20, 30))
        {
         Print("Failed to create header label.");
         return false;
        }
      m_lblHeader.Text("TrendLoom EA");
      m_lblHeader.Color(PanelHeaderColor);
      m_lblHeader.FontSize(14);
      m_lblHeader.Font("Segoe UI");
      Add(m_lblHeader);
      // Set header text alignment to center using ObjectSetInteger
      if(!ObjectSetInteger(0L, m_lblHeader.Name(), OBJPROP_ALIGN, (long)ALIGN_CENTER))
         Print("Failed to set header alignment");

      // Create main panel background
      int panelBottom = 50 + (btnHeight + btnSpacing) * 7 + btnSpacing;
      if(!m_panelMain.Create(0, "TrendLoomPanel", 0, 10, 50, x2 - x1 - 10, panelBottom))
        {
         Print("Failed to create main panel.");
         return false;
        }
      m_panelMain.Color(PanelBackgroundColor);
      m_panelMain.BorderType(BORDER_RAISED);
      m_panelMain.ColorBorder(clrSilver);
      Add(m_panelMain);

      // Starting coordinates for buttons
      int btnX = 20; // relative to dialog
      int btnY = 60;
      int buttonWidth = btnWidth;
      int buttonHeight = btnHeight;

      // Create each button with a modern look
      if(!m_btnShortTerm.Create(0, buttonNames[0], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnShortTerm.Text(buttonTexts[0]);
      m_btnShortTerm.Font("Segoe UI");
      m_btnShortTerm.FontSize(12);
      m_btnShortTerm.Color(ButtonBgColor);
      Add(m_btnShortTerm);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnScalping.Create(0, buttonNames[1], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnScalping.Text(buttonTexts[1]);
      m_btnScalping.Font("Segoe UI");
      m_btnScalping.FontSize(12);
      m_btnScalping.Color(ButtonBgColor);
      Add(m_btnScalping);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnSwing.Create(0, buttonNames[2], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnSwing.Text(buttonTexts[2]);
      m_btnSwing.Font("Segoe UI");
      m_btnSwing.FontSize(12);
      m_btnSwing.Color(ButtonBgColor);
      Add(m_btnSwing);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnTrend.Create(0, buttonNames[3], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnTrend.Text(buttonTexts[3]);
      m_btnTrend.Font("Segoe UI");
      m_btnTrend.FontSize(12);
      m_btnTrend.Color(ButtonBgColor);
      Add(m_btnTrend);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnMTFTrend.Create(0, buttonNames[4], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnMTFTrend.Text(buttonTexts[4]);
      m_btnMTFTrend.Font("Segoe UI");
      m_btnMTFTrend.FontSize(12);
      m_btnMTFTrend.Color(ButtonBgColor);
      Add(m_btnMTFTrend);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnShortScalper.Create(0, buttonNames[5], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnShortScalper.Text(buttonTexts[5]);
      m_btnShortScalper.Font("Segoe UI");
      m_btnShortScalper.FontSize(12);
      m_btnShortScalper.Color(ButtonBgColor);
      Add(m_btnShortScalper);
      btnY += buttonHeight + btnSpacing;

      if(!m_btnLongTerm.Create(0, buttonNames[6], 0, btnX, btnY, btnX + buttonWidth, btnY + buttonHeight))
         return false;
      m_btnLongTerm.Text(buttonTexts[6]);
      m_btnLongTerm.Font("Segoe UI");
      m_btnLongTerm.FontSize(12);
      m_btnLongTerm.Color(ButtonBgColor);
      Add(m_btnLongTerm);
      btnY += buttonHeight + btnSpacing;

      // Create results label below the buttons
      if(!m_lblResults.Create(0, "TrendResults", 0, btnX, btnY, btnX + buttonWidth, btnY + 30))
         return false;
      m_lblResults.Text("Analysis Result: [Waiting for Input]");
      m_lblResults.Font("Segoe UI");
      m_lblResults.FontSize(12);
      m_lblResults.Color(AnalysisTextColor);
      Add(m_lblResults);
      // Set results text alignment to left using ObjectSetInteger
      if(!ObjectSetInteger(0L, m_lblResults.Name(), OBJPROP_ALIGN, (long)ALIGN_LEFT))
         Print("Failed to set results alignment");

      Show();
      return true;
     }

   // Process events (button clicks)
   bool              OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
     {
      if(sparam == "btnShortTerm")
        {
         string res = AnalyzeTimeframes(PERIOD_M1, PERIOD_M5, PERIOD_M15);
         string out = "Short Term Focus: " + res;
         UpdateResults(out);
         Alert(out);
         return true;
        }
      else
         if(sparam == "btnScalping")
           {
            string res = AnalyzeTimeframes(PERIOD_M5, PERIOD_M15, PERIOD_H1);
            string out = "Scalping/Intraday: " + res;
            UpdateResults(out);
            Alert(out);
            return true;
           }
         else
            if(sparam == "btnSwing")
              {
               string res = AnalyzeTimeframes(PERIOD_M15, PERIOD_H1, PERIOD_H4);
               string out = "Swing Trading: " + res;
               UpdateResults(out);
               Alert(out);
               return true;
              }
            else
               if(sparam == "btnTrend")
                 {
                  string res = AnalyzeTimeframes(PERIOD_H1, PERIOD_H4, PERIOD_D1);
                  string out = "Trend Trading: " + res;
                  UpdateResults(out);
                  Alert(out);
                  return true;
                 }
               else
                  if(sparam == "btnMTFTrend")
                    {
                     string res = AnalyzeTimeframes(PERIOD_H1, PERIOD_H4, PERIOD_W1);
                     string out = "MTF Trend Confirm: " + res;
                     UpdateResults(out);
                     Alert(out);
                     return true;
                    }
                  else
                     if(sparam == "btnShortScalper")
                       {
                        string res = AnalyzeTimeframes(PERIOD_M5, PERIOD_H1, PERIOD_D1);
                        string out = "Short Scalper/Mid Trend: " + res;
                        UpdateResults(out);
                        Alert(out);
                        return true;
                       }
                     else
                        if(sparam == "btnLongTerm")
                          {
                           string res = AnalyzeTimeframes(PERIOD_H1, PERIOD_D1, PERIOD_W1);
                           string out = "Long Term Trend: " + res;
                           UpdateResults(out);
                           Alert(out);
                           return true;
                          }
      return false;
     }

   bool              ChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
     {
      return OnEvent(id, lparam, dparam, sparam);
     }

   // Update the results label and refresh the chart
   void              UpdateResults(const string &result)
     {
      m_lblResults.Text("Analysis Result: " + result);
      ChartRedraw();
     }
  };

// Global instance of the TrendLoom Panel
CTrendLoomPanel TrendPanel;

//------------------------------------------------------------------------------
// Helper functions (core analysis logic)
//------------------------------------------------------------------------------
double GetSMA(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift)
  {
   int handle = iMA(symbol, timeframe, period, 0, MODE_SMA, PRICE_CLOSE);
   if(handle == INVALID_HANDLE)
     {
      Print("Failed to create iMA handle for timeframe ", timeframe);
      return 0.0;
     }
   double sma[];  // dynamic array to store SMA values
   if(CopyBuffer(handle, 0, shift, 1, sma) <= 0)
     {
      Print("Failed to copy buffer for timeframe ", timeframe);
      IndicatorRelease(handle);
      return 0.0;
     }
   double result = sma[0];
   IndicatorRelease(handle);
   return result;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string AnalyzeTimeframes(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2, ENUM_TIMEFRAMES tf3)
  {
   int period = 50;
   int shift  = 1; // last completed candle

   double price1 = iClose(_Symbol, tf1, shift);
   double sma1   = GetSMA(_Symbol, tf1, period, shift);
   int signal1   = (price1 > sma1) ? 1 : (price1 < sma1 ? -1 : 0);

   double price2 = iClose(_Symbol, tf2, shift);
   double sma2   = GetSMA(_Symbol, tf2, period, shift);
   int signal2   = (price2 > sma2) ? 1 : (price2 < sma2 ? -1 : 0);

   double price3 = iClose(_Symbol, tf3, shift);
   double sma3   = GetSMA(_Symbol, tf3, period, shift);
   int signal3   = (price3 > sma3) ? 1 : (price3 < sma3 ? -1 : 0);

   int sum = signal1 + signal2 + signal3;
   if(sum >= 2)
      return "BUY";
   else
      if(sum <= -2)
         return "SELL";
      else
         return "NEUTRAL";
  }

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(!TrendPanel.CreateTrendPanel(0, "TrendLoom Panel", PanelX, PanelY, PanelX + PanelWidth + 20, PanelY + 400))
     {
      Print("Failed to create TrendLoom Panel.");
      return INIT_FAILED;
     }
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   TrendPanel.Destroy(reason);
  }

//+------------------------------------------------------------------+
//| Chart Event Handler                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   TrendPanel.ChartEvent(id, lparam, dparam, sparam);
  }
//+------------------------------------------------------------------+


結果

すべてのトレーダーにとって、システムを実際の取引で使用する前に十分にテストすることは非常に重要です。テストでは、過去のデータを使ったバックテストを行い、資金をリスクにさらすことなくシステムのパフォーマンスを確認できます。また、デモライブ口座を使ってリアルタイムの実際の動きを観察することも可能です。このプロセスにより、より信頼性の高いツールを調整・開発し、自信を持ってライブ口座で使えるようになります。私自身は、EAのテストと改良にかなりの時間を費やし、より堅牢な結果を追求しています。

このセクションでは、チャート上でEAを稼働させたテスト結果を紹介します。Volatility 75 (1s)でテストしたところ、素晴らしく利益を生む結果となりました。すべてのボタンが意図した通りに動作し、ボタンを押すと分析結果がほぼ即時に更新されます。以下に最初のテストを振り返ってみましょう。

図4:Volatility 75 (1s)テスト

以下の図は、提示されたシグナルに基づいて取引を行った後の市場の動きを示しています。この図は、上記のGIFで示した取引の続きです。取引の全体像を把握しやすくするために、M1の時間枠を使用しています。

図5:V 75(1秒)テスト


結論

EAを作成・テストした結果、市場分析に対してポジティブな効果があることを自信を持って確認できます。迅速なシグナル処理と全体的なトレンド評価により、ボラティリティインデックスで強力な成果を上げました。ただし、このツールは最終的なシグナル発信源というよりも補助的な支援ツールとして機能します。ご自身で十分にテストし、パラメータを好みに合わせて調整することをおすすめします。また、ボタンの見た目をカスタマイズするためにさらに改良することも可能です。全体的な取引戦略の確認にぜひ活用してください。私自身、その使い方で効果を実感しています。

日付 ツール名  詳細 バージョン  アップデート  備考
01/10/24 ChartProjector 前日のプライスアクションをゴースト効果でオーバーレイするスクリプト 1.0 初回リリース ツール番号1
18/11/24 Analytical Comment 前日の情報を表形式で提供し、市場の将来の方向性を予測する 1.0 初回リリース ツール番号2
27/11/24 Analytics Master 2時間ごとに市場指標を定期的に更新  1.01 v.2 ツール番号3
02/12/24 Analytics Forecaster  Telegram統合により、2時間ごとに市場指標を定期的に更新 1.1 v.3 ツール番号4
09/12/24 Volatility Navigator ボリンジャーバンド、RSI、ATR指標を使用して市場の状況を分析するEA 1.0 初回リリース ツール番号5
19/12/24 Mean Reversion Signal Reaper  平均回帰戦略を用いて市場を分析し、シグナルを提供する  1.0  初回リリース  ツール番号6 
9/01/25  Signal Pulse  多時間枠分析ツール 1.0  初回リリース  ツール番号7 
17/01/25  Metrics Board  分析用のボタン付きパネル  1.0  初回リリース ツール番号8 
21/01/25 External Flow 外部ライブラリによる分析 1.0  初回リリース ツール番号9 
27/01/25 VWAP 出来高加重平均価格   1.3  初回リリース  ツール番号10 
02/02/25  Heikin Ashi  トレンドの平滑化と反転シグナルの識別  1.0  初回リリース  ツール番号11
04/02/25  FibVWAP  Python分析によるシグナル生成  1.0  初回リリース  ツール番号12
14/02/25  RSI DIVERGENCE  プライスアクションとRSIのダイバージェンス  1.0  初回リリース  ツール番号13 
17/02/25  Parabolic Stop and Reverse (PSAR)  PSAR戦略の自動化 1.0 初回リリース  ツール番号14
20/02/25  Quarters Drawerスクリプト  チャートにクォーターレベルを描く  1.0  初回リリース  ツール番号15 
27/02/25  Intrusion Detector 価格がクォーターレベルに達したときに検出して警告する 1.0   初回リリース ツール番号16 
27/02/25  TrendLoom Tool  タ時間枠分析パネル 1.0 初回リリース ツール番号17

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

添付されたファイル |
TrendLoom_EA.mq5 (26.63 KB)
最後のコメント | ディスカッションに移動 (3)
linfo2
linfo2 | 21 3月 2025 において 03:01
ワオ、ファンタスティック!これに多くの努力が注がれているのが分かる。
Christian Benjamin
Christian Benjamin | 21 3月 2025 において 11:21
linfo2 #:
ワオ、ファンタスティック!これに多くの努力が注がれているのがわかるよ。
どういたしまして。こちらこそ、ご連絡ありがとうございます👏。
gardee005
gardee005 | 25 3月 2025 において 16:49
あなたの仕事が本当に好きで、ずっと見守ってきた!
MQL5取引ツールキット(第8回):コードベースにHistory Manager EX5ライブラリを実装して使用する方法 MQL5取引ツールキット(第8回):コードベースにHistory Manager EX5ライブラリを実装して使用する方法
MetaTrader 5口座の取引履歴を処理するために、MQL5ソースコード内で「History Manager EX5」ライブラリを簡単にインポートして活用する方法を、本連載の最終回となるこの記事で解説します。MQL5ではシンプルな1行の関数呼び出しで、取引データの管理や分析を効率的におこなうことが可能です。さらに、取引履歴の分析スクリプトを複数作成する方法や、実用的なユースケースとして、価格ベースのエキスパートアドバイザー(EA)の開発方法についても学んでいきます。このEAは、価格データとHistory Manager EX5ライブラリを活用し、過去のクローズ済み取引に基づいて取引判断をおこない、取引量の調整やリカバリーストラテジーの実装をおこないます。
プライスアクション分析ツールキットの開発(第16回):クォーターズ理論の紹介(II) - Intrusion Detector EA プライスアクション分析ツールキットの開発(第16回):クォーターズ理論の紹介(II) - Intrusion Detector EA
前回の記事では、「Quarters Drawer」というシンプルなスクリプトを紹介しました。このツールを基盤として、今回はさらに一歩進め、これらのクォーターを監視し、市場がどのように反応するかを見極めるためのモニター型エキスパートアドバイザー(EA)を作成します。本記事では、ゾーン検出ツールの開発プロセスについて紹介します。
MQL5での取引戦略の自動化(第11回):マルチレベルグリッド取引システムの開発 MQL5での取引戦略の自動化(第11回):マルチレベルグリッド取引システムの開発
本記事では、MQL5を使用してマルチレベルのグリッド取引システムEAを開発し、グリッド取引戦略の背後にあるアーキテクチャとアルゴリズム設計に焦点を当てます。複数層にわたるグリッドロジックの実装と、市場のさまざまな状況に対応するためのリスク管理手法について探ります。最後に、自動売買システムの構築・テスト・改善をおこなうための詳細な説明と実践的なヒントを提供します。
知っておくべきMQL5ウィザードのテクニック(第56回):ビル・ウィリアムズフラクタル 知っておくべきMQL5ウィザードのテクニック(第56回):ビル・ウィリアムズフラクタル
ビル・ウィリアムズによるフラクタルは、最初にチャート上で目にしたときには見落とされがちな強力なインジケーターです。一見するとチャートが煩雑に見え、鋭さに欠けるように思えるかもしれません。この記事では、このインジケーターの覆いを取り払い、そのさまざまなパターンがどのように機能するのかを、MQL5ウィザードで組み上げたエキスパートアドバイザー(EA)によるフォワードウォークテストを通じて検証していきます。