价格行为分析工具包开发(第八部分):指标看板(基础篇)
「指标看板在 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 图上拉出一个空面板验证编译是否通过——外汇与贵金属杠杆高,仅验证框架不涉任何交易信号。
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 个分析函数和一个均线计算函数,外加一个关闭按钮的点击事件处理函数,说明面板打算把高低点、成交量、趋势、波动率、均线和支撑阻力一次性铺开。
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); };
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; }
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 个,要加新指标直接往数组里补一行就行。
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++) {