价格行为分析工具包开发(第八部分):指标看板(基础篇)
📘

价格行为分析工具包开发(第八部分):指标看板(基础篇)

第 1/3 篇

「指标看板在 MT5 里的落地形态」

把多品种、多周期的关键指标集中到一个面板,是价格行为分析里减少切图损耗的直接办法。MT5 自带的 Observatory 思路可以借来用:用 CAppDialog 派生一个面板类,把指标值通过 CLabel 或 CEdit 控件刷出来。 在 2025-09-29 发布的这套看板原型里,作者 Christian Benjamin 把系统拆成包含函数库 + 主 EA 两部分,面板本身不画新指标,只做已有指标的聚合显示。实测在 894 次社区浏览量下,零评论说明多数人来抄代码而非讨论逻辑——这反而提示:能跑起来的面板比能说服人的文档更有价值。 开 MT5 后,你可以先建一个 CAppDialog 派生类,挂到图表左上角,用 EventChartCustom 把指标句柄的值推给面板刷新。外汇与贵金属杠杆高、滑点随机,面板只降低盯盘成本,不替代信号判断。

面板EA把六类分析塞进一个界面

MetaTrader 5 里跑的指标面板智能交易系统(EA),把原先分散的市场分析动作收敛成一个集成界面。点一下对应按钮就能拿到实时计算结果,不用自己切图表、加载多个指标再手动比对。 它覆盖了六块:高低点检测关键价格水平、成交量看参与度与流动性、趋势评方向强度、波动率量化环境、移动平均线跟动态价格、支撑阻力定进出场。对做价格行为的人,前两项和最后一项最常用,因为反转点和风控位基本靠它们定位。 底层用算法跑计算,强调速度和准确,定位是给专业交易者省时间。外汇和贵金属波动大、杠杆高,这类工具只负责把数据转成可读结论,不替你做买卖决定,信号错了照样可能亏。

◍ 面板型EA的四段式运转骨架

这套EA本质是一个带按钮的对话框工具,不是传统挂在图表上的画线指标。类初始化时直接生成若干分析按钮,用户点哪个就跑哪段逻辑,和MT5里手动调指标的最大区别是把多套分析塞进同一个面板。 事件分发很直白:按钮被点击 → 触发对应的分析方法 → 市场数据在面板内处理并渲染。整个链路只有「类设置 / 事件处理 / 分析与展示 / 关闭操作」四段,关闭按钮单纯销毁面板,不碰图表对象。 从工程角度看,这种结构把「分析触发」和「结果呈现」解耦了:你能在不重绘图表的情况下反复切换分析视角。想在MT5验证,直接新建一个dialog类继承CWndDialog,挂三个按钮看消息映射是否跑通即可。外汇与贵金属市场波动剧烈,此类工具仅辅助判断,实盘仍需自担高风险。

「指标面板类的骨架代码」

下面这段 MQL5 类定义搭出了一个可停靠在图表上的指标面板(CMetricsBoard),它继承自标准库里的 CAppDialog,意味着你不用自己写窗口拖拽和最小化逻辑。 类里先声明了 7 个 CButton 私有成员:m_btnClose 管关闭,其余六个分别对应高低点分析、成交量、趋势、波动率、均线和支撑阻力——也就是说面板预设了六类价格行为分析入口。 还挂了一个 CPanel 和 CLabel(m_panelResults / m_lblResults),用来承载分析结果的文本输出区域。 公开方法里值得注意 Run() 和 OnEvent() 都是 virtual,说明子类或外部调用可以重写事件流转;Destroy() 显式带默认参数 REASON_PROGRAM,覆盖基类销毁行为,避免面板被误关时资源没释放干净。 把这段代码直接贴进 MT5 的 MQ5 文件,配合后面要写的 CreateButtons() 实现,就能在 EURUSD 的 M5 图上拉出一个空面板验证编译是否通过——外汇与贵金属杠杆高,仅验证框架不涉任何交易信号。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 |
class=class="str">"cmt">//|                     Metrics Board.mql5|
class=class="str">"cmt">//|     Copyright class="num">2025, Christian Benjamin|
class=class="str">"cmt">//|                     [MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "class="num">2025, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.0"
class="macro">#class="kw">property strict
class="macro">#include <Trade\Trade.mqh>
class="macro">#include <Controls\Dialog.mqh>
class="macro">#include <Controls\Button.mqh>
class="macro">#include <Controls\Label.mqh>
class="macro">#include <Controls\Panel.mqh>
class=class="str">"cmt">// Metrics Board Class
class CMetricsBoard : class="kw">public CAppDialog
  {
class="kw">private:
   CButton            m_btnClose; class=class="str">"cmt">// Close Button
   CButton            m_btnHighLowAnalysis;
   CButton            m_btnVolumeAnalysis;
   CButton            m_btnTrendAnalysis;
   CButton            m_btnVolatilityAnalysis;
   CButton            m_btnMovingAverage;
   CButton            m_btnSupportResistance;
   CPanel             m_panelResults;
   CLabel             m_lblResults;
class="kw">public:
                     CMetricsBoard(class="type">void);
                    ~CMetricsBoard(class="type">void);
   class="kw">virtual class="type">bool      Create(const class="type">long chart, const class="type">class="kw">string name, const class="type">int subwin, const class="type">int x1, const class="type">int y1, const class="type">int x2, const class="type">int y2);
   class="kw">virtual class="type">void      Minimize();
   class="kw">virtual class="type">bool      Run(); class=class="str">"cmt">// Declaration of Run method
   class="kw">virtual class="type">bool      OnEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam);
   class="kw">virtual class="type">bool      ChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam);
   class="kw">virtual class="type">void      Destroy(const class="type">int reason = REASON_PROGRAM); class=class="str">"cmt">// Override Destroy method
class="kw">private:
   class="type">bool              CreateButtons(class="type">void);

指标面板类的骨架与对话框创建逻辑

在 MT5 里做一个悬浮指标面板,通常从继承 CAppDialog 开始。下面这段类声明里,CMetricsBoard 除了析构和构造留空,还挂了 8 个分析函数和一个均线计算函数,外加一个关闭按钮的点击事件处理函数,说明面板打算把高低点、成交量、趋势、波动率、均线和支撑阻力一次性铺开。

MQL5 / C++
class="type">bool CreateResultsPanel(class="type">void);
class="type">void OnClickButtonClose(); class=class="str">"cmt">// New close button handler
class="type">void PerformHighLowAnalysis(class="type">void);
class="type">void PerformVolumeAnalysis(class="type">void);
class="type">void PerformTrendAnalysis(class="type">void);
class="type">void PerformVolatilityAnalysis(class="type">void);
class="type">void PerformMovingAverageAnalysis(class="type">void);
class="type">void PerformSupportResistanceAnalysis(class="type">void);
class="type">class="kw">double CalculateMovingAverage(class="type">int period);
};
Create 函数是真正把面板推到图表上的入口。它先调基类 CAppDialog::Create 用 chart/name/subwin 和四角坐标 (x1,y1,x2,y2) 建对话框,任一子步骤失败就 Print 错误并返回 false;三个环节——对话框本体、结果面板、按钮——全部成功后才 Show() 并返 true。
MQL5 / C++
class="type">bool CMetricsBoard::Create(const class="type">long chart, const class="type">class="kw">string name, const class="type">int subwin, const class="type">int x1, const class="type">int y1, const class="type">int x2, const class="type">int y2)
  {
   if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2))
     {
      Print("Failed to create CAppDialog instance.");
      class="kw">return false;
     }
   if(!CreateResultsPanel())
     {
      Print("Failed to create results panel.");
      class="kw">return false;
     }
   if(!CreateButtons())
     {
      Print("Failed to create buttons.");
      class="kw">return false;
     }
   Show();
   class="kw">return true;
  }
Destroy 里只补了一行 CAppDialog::Destroy(reason),确保关闭面板时基类资源被释放,避免 MT5 终端里残留不可见的窗口句柄。Minimize 直接转调基类,说明最小化行为和标准对话框一致,无需重写逻辑。外汇与贵金属波动剧烈,这类自定义面板仅作辅助参考,实盘信号仍须结合仓位与止损。

MQL5 / C++
class="type">bool              CreateResultsPanel(class="type">void);
   class="type">void              OnClickButtonClose(); class=class="str">"cmt">// New close button handler
   class="type">void              PerformHighLowAnalysis(class="type">void);
   class="type">void              PerformVolumeAnalysis(class="type">void);
   class="type">void              PerformTrendAnalysis(class="type">void);
   class="type">void              PerformVolatilityAnalysis(class="type">void);
   class="type">void              PerformMovingAverageAnalysis(class="type">void);
   class="type">void              PerformSupportResistanceAnalysis(class="type">void);
   class="type">class="kw">double            CalculateMovingAverage(class="type">int period);
};
CMetricsBoard::CMetricsBoard(class="type">void) {}
CMetricsBoard::~CMetricsBoard(class="type">void) {}
class=class="str">"cmt">// Override Destroy method
class="type">void CMetricsBoard::Destroy(const class="type">int reason)
  {
class=class="str">"cmt">// Call base class Destroy method to release resources
   CAppDialog::Destroy(reason);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Create a control dialog                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CMetricsBoard::Create(const class="type">long chart, const class="type">class="kw">string name, const class="type">int subwin, const class="type">int x1, const class="type">int y1, const class="type">int x2, const class="type">int y2)
  {
   if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2))
     {
      Print("Failed to create CAppDialog instance.");
      class="kw">return false; class=class="str">"cmt">// Failed to create the dialog
     }
   if(!CreateResultsPanel())
     {
      Print("Failed to create results panel.");
      class="kw">return false; class=class="str">"cmt">// Failed to create the results panel
     }
   if(!CreateButtons())
     {
      Print("Failed to create buttons.");
      class="kw">return false; class=class="str">"cmt">// Failed to create buttons
     }
   Show(); class=class="str">"cmt">// Show the dialog after creation
   class="kw">return true; class=class="str">"cmt">// Successfully created the dialog
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Minimize the control window                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CMetricsBoard::Minimize()
  {
   CAppDialog::Minimize();
  }

◍ 把面板和按钮真正挂到图上

CMetricsBoard 的 Run() 只做一件事:先尝试 Show() 把对话框拉起来,失败就打印 Failed to show the control. 并返回 false,成功才返回 true。这意味着面板可见性完全依赖基类窗口资源是否就绪,开 MT5 跑之前先确认指标附属窗口没被手动关掉。 CreateResultsPanel() 用 m_panelResults.Create(0,"ResultsPanel",0,10,10,330,60) 在坐标 (10,10) 画了 330×60 的浅灰底板,里面再塞一个 315×30、字号 12 的黑色标签,初始写着 Results will be displayed here.。这个 330 宽度在 1920 屏下够用,但在笔记本 1366 分辨率里可能贴边,可调成 300 留余量。 CreateButtons() 从 (20,80) 开始纵向排按钮:关闭键占 300×30,之后每键下移 30+15 间距。High/Low、Volume、Trend、Volatility、MA、Support/Resistance 六个分析键被塞进 ButtonData 结构体数组,用 for 循环批量 Create,循环上限由 ArraySize(buttons) 决定——目前是 6 个,要加新指标直接往数组里补一行就行。

MQL5 / C++
class="type">bool CMetricsBoard::Run()
  {
class=class="str">"cmt">// Assuming Run makes the dialog functional
   if(!Show())
     {
       Print("Failed to show the control.");
       class="kw">return false; class=class="str">"cmt">// Could not show the control
     }
class=class="str">"cmt">// Additional initialization or starting logic can be added here
   class="kw">return true; class=class="str">"cmt">// Successfully run the control
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Create the results panel                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CMetricsBoard::CreateResultsPanel(class="type">void)
  {
   if(!m_panelResults.Create(class="num">0, "ResultsPanel", class="num">0, class="num">10, class="num">10, class="num">330, class="num">60))
     class="kw">return false;
   m_panelResults.Color(clrLightGray);
   Add(m_panelResults);
   if(!m_lblResults.Create(class="num">0, "ResultsLabel", class="num">0, class="num">15, class="num">15, class="num">315, class="num">30))
     class="kw">return false;
   m_lblResults.Text("Results will be displayed here.");
   m_lblResults.Color(clrBlack);
   m_lblResults.FontSize(class="num">12);
   Add(m_lblResults);
   class="kw">return true;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Create buttons for the panel                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CMetricsBoard::CreateButtons(class="type">void)
  {
   class="type">int x = class="num">20;
   class="type">int y = class="num">80;
   class="type">int buttonWidth = class="num">300;
   class="type">int buttonHeight = class="num">30;
   class="type">int spacing = class="num">15;
class=class="str">"cmt">// Create Close Button
   if(!m_btnClose.Create(class="num">0, "CloseButton", class="num">0, x, y, x + buttonWidth, y + buttonHeight))
     class="kw">return false;
   m_btnClose.Text("Close Panel");
   Add(m_btnClose);
   y += buttonHeight + spacing;
   class="kw">struct ButtonData
     {
       CButton       *button;
       class="type">class="kw">string         name;
       class="type">class="kw">string         text;
     };
   ButtonData buttons[] =
     {
       {&m_btnHighLowAnalysis, "HighLowButton", "High/Low Analysis"},
       {&m_btnVolumeAnalysis, "VolumeButton", "Volume Analysis"},
       {&m_btnTrendAnalysis, "TrendButton", "Trend Analysis"},
       {&m_btnVolatilityAnalysis, "VolatilityButton", "Volatility Analysis"},
       {&m_btnMovingAverage, "MovingAverageButton", "Moving Average"},
       {&m_btnSupportResistance, "SupportResistanceButton", "Support/Resistance"}
     };
   for(class="type">int i = class="num">0; i < ArraySize(buttons); i++)
     {

常见问题

用面板型EA把六类分析塞进一个界面,通过四段式骨架初始化、创建面板、刷新数据、响应点击,开盘前加载即可集中盯盘。
可能和指标刷新频率有关,建议在骨架的刷新段用定时器控制重算间隔,而非每帧都跑全部指标。
小布可读取品种页已内置的六类分析面板,自动标出背离与临界突破,你只需点开对应品种页看告警。
多在对话框创建逻辑里没把按钮对象设为可点击或没绑定ChartEvent,检查面板类的Create函数返回值与事件分发。
不能。贵金属高风险,面板仅作概率倾向参考,实盘须自设止损并人工确认,勿直接跟信号下单。