English Русский Deutsch 日本語
preview
价格行为分析工具包开发(第 17 部分):TrendLoom EA 工具

价格行为分析工具包开发(第 17 部分):TrendLoom EA 工具

MetaTrader 5交易系统 |
515 3
Christian Benjamin
Christian Benjamin

内容



引言

趋势分析师的市场分析和入场确认方法各不相同。许多交易者会查看多个时间周期,例如 M1、M5 和 M15,或 H1、H4 和 W1,以验证他们的入场并提高信号的可靠性。您无需切换时间周期来评估整体趋势,只需按一下按钮,即可获得及时的更新,或自动获取更新。您是否曾遇到过这样的情况:在较低时间周期上看到卖出信号并入场交易,然后打开较高时间周期的图表,却发现那是一个买入趋势?

TrendLoom EA 就是为了防止这种错误而设计的。它的特色是一个包含七个按钮的面板,这些按钮代表了不同的交易风格。每个按钮显示三个时间周期,这些时间周期会一起使用移动平均线进行分析,以生成买入、卖出或中性信号。这个强大的工具提供快速的确认更新,并在检测到相关信号时持续刷新。


策略概览

TrendLoom EA 的结构是一个图形界面(面板)。该面板包含七个按钮,每个按钮对应一个特定的交易策略。
  • 短期关注 (M1, M5, M15)
  • 剥头皮/日内交易 (M5, M15, H1)
  • 波段交易 (M15, H1, H4)
  • 趋势交易 (H1, H4, D1)
  • 多时间周期趋势确认 (H1, H4, W1)
  • 短期剥头皮/中期趋势 (M5, H1, D1)
  • 长期趋势 (H1, D1, W1)

让我们来逐步了解当按下按钮时,EA 如何生成买入、卖出或中性信号。

  • 收集数据:对于三个时间周期中的每一个(例如 M1、M5 和 M15),EA 会获取最后一根已完整形成 K 线的收盘价。
  • 计算 SMA:对于每个时间周期,EA 会计算一个 50 周期的简单移动平均线(SMA)。这个 SMA 作为当前价格的基准。
生成单个信号:EA 将收盘价与相应的 SMA 进行比较。

  • 如果价格高于 SMA,则视为看涨信号,并赋值为 +1。
  • 如果价格低于 SMA,则视为看跌信号,并赋值为 -1。
  • 当价格等于 SMA 时,信号为中性(0)。

组合信号

  • 将三个单独的信号(每个时间周期一个)相加。
  • 确定最终信号:
  • 如果总和为 2 或更高,则表明有强烈的看涨势头。EA 返回“买入”信号。
  • 如果总和为 -2 或更低,则表明有强烈的看跌势头。EA 返回“卖出”信号。
  • 否则,信号为混合或中性,因此 EA 返回“中性”。

让我们查看下面的图表以更好地理解该过程。

流程图

图 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/en/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 控制,EA 允许您自定义布局和颜色。这种配置提供了灵活性,使您可以根据需要定制用户界面。

//---- 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"

接着,代码声明了两个辅助函数:GetSMA 用于计算简单移动平均线,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);

自定义类 CTrendLoomPanel 继承自 CAppDialog。它将所有用户界面元素(如标题标签、主面板、按钮和结果标签)组合在一起。这种设计创建了一个模块化的界面,更易于管理和扩展。

创建面板

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;
}

更新用户界面

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 函数获取三个时间周期的收盘价和 SMA,当价格高于 SMA 时分配看涨信号,当价格低于 SMA 时分配看跌信号。它将各个信号相加,以产生最终建议:当总和为 2 或更高时为“买入”,当总和为 -2 或更低时为“卖出”,否则为“中性”。每个时间周期被独立评估,以捕捉市场趋势的平衡视图,而 shift 参数确保只使用最后完成的 K 线进行分析。结合来自多个时间周期的信号可以减少短暂市场噪音的影响,而调整 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/en/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 (1s) 测试


结论

在创建并测试了该 EA 后,我可以自信地确认它对市场分析产生了积极的影响。其快速的信号处理和整体趋势评估在波动率指数上产生了强大的效果。然而,该工具是作为一种辅助工具,而不是最终的信号提供者。我鼓励您对其进行彻底测试,并根据您的偏好调整参数。您也可以进一步修改它,以自定义按钮的外观。用它来确认您的整体策略,我已经看到它在这方面是有效的。

日期 工具名称  说明 版本  更新  提示
01/10/24 图表投影仪 用于叠加前一天价格走势(带“幽灵”效果)的脚本。 1.0 首次发布 工具1
18/11/24 分析评论 它以表格形式提供前一日的市场信息,并预测市场的未来走向。 1.0 首次发布 工具2
27/11/24 分析大师 市场指标每两小时定期更新  1.01 第二版 工具3
02/12/24 分析预测器  每两小时定期更新市场指标,并集成Telegram推送功能。 1.1 第三版 工具4
09/12/24 波动性导航工具 该EA使用布林带、RSI和ATR指标分析市场状况。 1.0 首次发布 工具5
19/12/24 均值回归信号收割器  使用均值回归策略分析市场并提供信号  1.0  首次发布  工具6 
9/01/25  信号脉冲  多时间框架分析器 1.0  首次发布  工具7 
17/01/25  指标看板  带按钮的分析面板  1.0  首次发布 工具8 
21/01/25 外部资金流 通过外部库进行分析 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 背离  价格走势与 RSI 背离的对比  1.0  首次发布  工具13 
17/02/25  抛物线转向与反转 (PSAR)  自动化PSAR策略 1.0 首次发布  工具14
20/02/25  四分位绘制脚本  在图表上绘制四分位水平线  1.0  首次发布  工具15 
27/02/25  侵入检测器 当价格触及四分位水平时进行检测和警报 1.0   首次发布 工具16 
27/02/25  TrendLoom工具  多时间周期分析面板 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
非常喜欢您的作品,一直在关注!
具有强化学习和灭绝失败个体的进化交易算法(ETARE) 具有强化学习和灭绝失败个体的进化交易算法(ETARE)
在本文中,我介绍了一种创新的交易算法,其针对外汇交易结合了进化算法与深度强化学习。该算法利用低效个体灭绝机制来优化交易策略。
从基础到中级:模板和类型名称(一) 从基础到中级:模板和类型名称(一)
在本文中,我们开始考虑许多初学者避免的概念之一。这与模板不是一个容易的话题有关,因为许多人不理解模板的基本原理:函数和过程的重载。
市场模拟(第三部分):性能问题 市场模拟(第三部分):性能问题
我们经常需要后退一步,然后继续前进。在本文中,我们将展示所有必要的更改,以确保鼠标和 Chart Trade 指标不会中断。作为奖励,我们还将介绍未来将广泛使用的其他头文件中发生的其他更改。
事后交易分析:在策略测试器中选择尾随停止和新的止损位 事后交易分析:在策略测试器中选择尾随停止和新的止损位
我们继续在策略测试器中分析已完结成交的主题,以便提升交易品质。我们看看使用不同的尾随停止如何改变我们现有的交易结果。