向指标或者EA中快速添加控制面板·进阶篇
🎛️

向指标或者EA中快速添加控制面板·进阶篇

(2/3)· 还在每次调参数都重启EA?用标准类库挂面板,实时改设置不中断策略

进阶 第 2/3 篇
很多老手把最猛的策略写进EA,却卡在改个参数就要重加载的笨办法上。面板不是装饰,是把输入参数搬到图表上实时拨动的关键件。本文接着基础篇,直接把标准类库的控件焊进你的程序。

控件对话框里的勾选与事件钩子

在 MT5 自定义面板里,复选组(check group)的状态读写靠两个虚函数承接:SetCheck 按索引写值,GetCheck 按索引返回值。下面这段来自 CControlsDialog 的实现,把对 m_check_group 的调用包了一层,外部只需传 idx 和布尔量。

MQL5 / C++
class="type">bool CControlsDialog::SetCheck(class="kw">const class="type">int idx,class="kw">const class="type">bool check)
  {
   class="kw">return(m_check_group.Check(idx,check));
  }
class="type">int CControlsDialog::GetCheck(class="kw">const class="type">int idx)
  {
   class="kw">return(m_check_group.Check(idx));
  }
逐行看:第一行声明 SetCheck 返回 bool,入参是元素索引和是否勾选;函数体直接转调底层 Check 方法并回传结果。GetCheck 只收索引,返回当前勾选态的 int(0/1),同样透传 m_check_group.Check(idx)。 图表事件必须接住才能把点击透给面板。OnChartEvent 里只做一件事:把 id、lparam、dparam、sparam 原样丢给 ExtDialog.ChartEvent。少了这层转发,复选框点了没反应。 输入参数那三行决定了通知渠道默认态:bln_alert 默认 true,bln_mail 和 bln_push 默认 false。也就是加载指标后不改设置,只弹终端 alert,不发邮件和手机推送。外汇与贵金属波动剧烈,这类面板仅作辅助提醒,信号误触或延迟都可能发生,实操请先在模拟盘验证。

MQL5 / C++
class="kw">virtual class="type">bool        OnEvent(class="kw">const class="type">int id,class="kw">const class="type">long &lparam,class="kw">const class="type">class="kw">double &dparam,class="kw">const class="type">class="kw">string &sparam);
  class=class="str">"cmt">//--- 元素的SetCheck()函数
  class="kw">virtual class="type">bool        SetCheck(class="kw">const class="type">int idx,class="kw">const class="type">int value);
  class=class="str">"cmt">//--- 元素的GetCheck()函数
  class="kw">virtual class="type">int         GetCheck(class="kw">const class="type">int idx) class="kw">const;
class="kw">protected:
  class=class="str">"cmt">//--- 创建独立控件
  class="type">bool                CreateCheckGroup(class="type">void);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 为元素设置检查
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CControlsDialog::SetCheck(class="kw">const class="type">int idx,class="kw">const class="type">bool check)
  {
   class="kw">return(m_check_group.Check(idx,check));
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 获取元素的检查结果
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CControlsDialog::GetCheck(class="kw">const class="type">int idx)
  {
   class="kw">return(m_check_group.Check(idx));
  }
class="macro">#class="kw">property indicator_chart_window
class="macro">#class="kw">property indicator_plots class="num">0
class="macro">#include "PanelDialog.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 全局变量                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
CControlsDialog ExtDialog;
class=class="str">"cmt">//--- 输入参数
class="kw">input class="type">bool     bln_mail=class="kw">false;      class=class="str">"cmt">// 通过email通知
class="kw">input class="type">bool     bln_push=class="kw">false;      class=class="str">"cmt">// 通过短信push通知
class="kw">input class="type">bool     bln_alert=true;      class=class="str">"cmt">// 通过alert通知
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| ChartEvent函数                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnChartEvent(class="kw">const class="type">int id,
                  class="kw">const class="type">long &lparam,
                  class="kw">const class="type">class="kw">double &dparam,
                  class="kw">const class="type">class="kw">string &sparam)
  {
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 指标缓存映射
class=class="str">"cmt">//--- 创建程序对话框

◍ 对话框初始化失败就直接退出

在 MT5 的 OnInit 阶段,如果自定义通知对话框没能建出来,最干脆的做法是立刻返回 INIT_FAILED,让 EA 或指标根本不起跑。上面这段把窗口建在屏幕坐标 (50,50)、宽 180 高 160,名字叫 "Notification",任何一步 Create 或 Run 不成功都直接拦死。 建完之后用 SetCheck 把三个开关按预设布尔量复位:索引 0 对应邮件(bln_mail)、1 对应推送(bln_push)、2 对应弹窗警报(bln_alert)。全部设完才返回 INIT_SUCCEEDED,意味着用户看到的第一帧界面状态已经是你代码里的默认配置。 开 MT5 自己验证时,可以把 Create 的第三参(x=50)改成 300,看窗口是否真的挪位;若故意把宽高设成 0,终端会报 INIT_FAILED,证明这段拦截逻辑是硬生效而非摆设。外汇与贵金属品种波动剧烈,这类通知开关若默认全开,可能在非农时段被消息刷屏,建议先按品种波动率手动调默认布尔值。

MQL5 / C++
if(!ExtDialog.Create(class="num">0,"Notification",class="num">0,class="num">50,class="num">50,class="num">180,class="num">160))
   class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 运行程序
if(!ExtDialog.Run())
   class="kw">return(INIT_FAILED);
class=class="str">"cmt">//---
ExtDialog.SetCheck(class="num">0,bln_mail);
ExtDialog.SetCheck(class="num">1,bln_push);
ExtDialog.SetCheck(class="num">2,bln_alert);
class=class="str">"cmt">//---
class="kw">return(INIT_SUCCEEDED);
}

「把MACD样例EA和参数面板拧成一股绳」

拿 MT5 自带的 MACD Sample.mq5 开刀,把它和 PanelDialog2.mqh 面板绑在一起,核心收益就一条:在图表上实时改 Lots、Trailing Stop Level 这些参数,点 Apply changes 立刻生效;而 Mail / Push / Alert 通知开关是自动生效的,不用再点按钮。外汇和贵金属波动剧烈,这种热改参数能让你在行情突变时少点几步鼠标,但仓位失控的风险也同步放大。 EA 侧先动刀:把标准样例复制到自定义 Notification 文件夹,在全局变量区加一套带 Ext 前缀的副本变量(ExtLots、ExtTakeProfit 等),OnInit 里把 Inp 开头的外部变量拷过去,再把 CSampleExpert 几个 Init 函数里的原变量替换成副本。编译零报错才算过关。 面板侧不能还是空壳。给 CControlsDialog 加外部变量存输入状态,构造函数初始化,Create 里按内部变量铺开关组。EA 里声明 ExtDialog 并加载面板,补上 OnChartEvent 和 OnDeinit——后者负责销毁面板,前者接图表事件。 两边真正对话靠 Initialization() 和 GetValues():EA 加载后把参数喂给面板,面板填好控件;点 Apply changes 后 mModification 置位,把改完的值回传 EA 重新初始化。最后一步防策略测试器里跑面板,加 bool_tester 标识,OnChartEvent 里侦测到参数变就热重启 EA 逻辑。 别在测试器里裸跑面板 面板依赖图表事件和可视控件,丢进策略测试器会直接失效。代码里用 bool_tester 在 OnInit 拦掉加载,实盘图表才允许 ExtDialog 起来,否则回测日志里只会看到一堆空引用。

MQL5 / C++
class="macro">#include <Trade\PositionInfo.mqh>
class="macro">#include <Trade\AccountInfo.mqh>
class=class="str">"cmt">//--- 输入参数
class="kw">input class="type">bool     InpMail=class="kw">false;           class=class="str">"cmt">//通过email进行通知
class="kw">input class="type">bool     InpPush=class="kw">false;           class=class="str">"cmt">// 通过短信push通知
class="kw">input class="type">bool     InpAlert=true;           class=class="str">"cmt">// 通过alert通知
class=class="str">"cmt">//---
class="kw">input class="type">class="kw">double InpLots          =class="num">0.1; class=class="str">"cmt">// 交易量
class="kw">input class="type">int    InpTakeProfit    =class="num">50;  class=class="str">"cmt">// 止赢(点数)
class="kw">input class="type">int    InpMACDCloseLevel=class="num">2;   class=class="str">"cmt">// MACD水平(以点数计)
class="kw">input class="type">int    InpMATrendPeriod =class="num">26;  class=class="str">"cmt">// MA周期
class=class="str">"cmt">//--- ext 变量
class="type">bool         ExtMail;
class="type">bool         ExtPush;
class="type">bool         ExtAlert;
class="type">class="kw">double       ExtLots;
class="type">int          ExtTakeProfit;
class="type">int          ExtTrailingStop;
class="type">int          ExtMACDOpenLevel;
class="type">int          ExtMACDCloseLevel;
class="type">int          ExtMATrendPeriod;
class=class="str">"cmt">//---
class="type">int ExtTimeOut=class="num">10; class=class="str">"cmt">// 交易操作之间的间隔时间(秒)
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| MACD 样例EA类
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA初始化函数                      
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit(class="type">void)
  {

EA 面板对象的参数绑定与初值

这段代码片段来自一个基于 MACD + MA 趋势的 EA 控制面板类,负责把外部输入参数接到内部成员变量上,并在构造时给一套默认交易设置。 先看参数映射部分:外部输入 InpMail、InpPush、InpAlert 等被直接赋给 ExtMail、ExtPush、ExtAlert 一类全局变量,随后通过 ExtExpert.Init() 创建所需图形对象;若初始化失败则不会继续。 类内部用 m 前缀成员变量承接策略参数:mLots 默认 0.1 手,mTakeProfit 默认 50 点,mTrailingStop 默认 30 点,mMACDOpenLevel 与 mMACDCloseLevel 分别为 3 点和 2 点,mMATrendPeriod 控制均线周期。mAlert_ 构造时置 true,mMail、mPush 置 false,意味着默认只弹本地警告不发邮件和推送。 在 MT5 里把这段逻辑塞进自己的面板类时,重点核对点数单位:若品种报价小数位为 5 位,50 点实际对应 0.0050 价格差,黄金等高风险品种点值异于直盘,参数照搬可能让止损过窄被秒扫。

MQL5 / C++
  ExtMail=InpMail;
  ExtPush=InpPush;
  ExtAlert=InpAlert;
  ExtLots=InpLots;
  ExtTakeProfit=InpTakeProfit;
  ExtTrailingStop=InpTrailingStop;
  ExtMACDOpenLevel=InpMACDOpenLevel;
  ExtMACDCloseLevel=InpMACDCloseLevel;
  ExtMATrendPeriod=InpMATrendPeriod;
class=class="str">"cmt">//--- 创建所有需要的对象
  if(!ExtExpert.Init())
class="kw">private:
  class=class="str">"cmt">//--- 元素的GetCheck()函数
  class="kw">virtual class="type">int       GetCheck(class="kw">const class="type">int idx);
  class=class="str">"cmt">//---
  class="type">bool              mMail;
  class="type">bool              mPush;
  class="type">bool              mAlert_;
  class="type">class="kw">double            mLots;             class=class="str">"cmt">// 交易量
  class="type">int               mTakeProfit;       class=class="str">"cmt">// 止赢(点数)
  class="type">int               mTrailingStop;     class=class="str">"cmt">// 追踪止损水平(点数)
  class="type">int               mMACDOpenLevel;    class=class="str">"cmt">// MACD开始水平(点数)
  class="type">int               mMACDCloseLevel;   class=class="str">"cmt">//MACD结束水平(点数)
  class="type">int               mMATrendPeriod;    class=class="str">"cmt">// MA 周期
  class=class="str">"cmt">//---
  class="type">bool              mModification;     class=class="str">"cmt">// 值是否改变
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 事件处理                                                          
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 构造函数                                                          
class=class="str">"cmt">//+------------------------------------------------------------------+
CControlsDialog::CControlsDialog(class="type">void) : mMail(class="kw">false),
                                       mPush(class="kw">false),
                                       mAlert_(true),
                                       mLots(class="num">0.1),
                                       mTakeProfit(class="num">50),
                                       mTrailingStop(class="num">30),
                                       mMACDOpenLevel(class="num">3),
                                       mMACDCloseLevel(class="num">2),

◍ EA初始化与通知面板的接线细节

这段代码片段展示了把 MACD 趋势 EA 和图形通知面板接起来的关键初始化逻辑。OnInit 里先把外部 input 参数逐一搬进全局变量,再调用 ExtExpert.Init() 和 ExtDialog.Create(0,"Notification",0,100,100,360,380) 建出 360×380 像素的悬浮对话框。 CreateButtonOK() 失败会直接 return(false),而 SetCheck(0,mMail)、SetCheck(1,mPush)、SetCheck(2,mAlert_) 三行决定了邮件、推送、弹窗三种通知渠道默认勾选状态,改这里就能调面板初始行为。 OnDeinit 用 ExtDialog.Destroy(reason) 释放资源并 Comment("") 清屏,避免残留文字干扰盯盘。外汇与贵金属杠杆高,EA 自动通知不代表信号可靠,实盘前请在 MT5 策略测试器跑通 Init/Deinit 生命周期。 想验证的话,把 InpMail 和 InpPush 设 true 再编译,面板第二项第三项应自动打勾;若 Create 返回 INIT_FAILED,优先查终端是否禁用了 DLL 或对象权限。

MQL5 / C++
mMATrendPeriod(class="num">26),
 mModification(class="kw">false)
{
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 析构函数
if(!CreateButtonOK())
   class="kw">return(class="kw">false);
class=class="str">"cmt">//---
  SetCheck(class="num">0,mMail);
  SetCheck(class="num">1,mPush);
  SetCheck(class="num">2,mAlert_);
class=class="str">"cmt">//--- 成功
  class="kw">return(true);
  }
class="macro">#include <Trade\PositionInfo.mqh>
class="macro">#include <Trade\AccountInfo.mqh>
class="macro">#include "PanelDialog2Original.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 全局变量
class=class="str">"cmt">//+------------------------------------------------------------------+
CControlsDialog ExtDialog;
class=class="str">"cmt">//--- 输入参数
class="kw">input class="type">bool   InpMail=class="kw">false;      class=class="str">"cmt">//通过email进行通知
class="kw">input class="type">bool   InpPush=class="kw">false;      class=class="str">"cmt">// 通过短信push通知
class="type">int OnInit(class="type">void)
  {
  ExtMail=InpMail;
  ExtPush=InpPush;
  ExtAlert=InpAlert;
  ExtLots=InpLots;
  ExtTakeProfit=InpTakeProfit;
  ExtTrailingStop=InpTrailingStop;
  ExtMACDOpenLevel=InpMACDOpenLevel;
  ExtMACDCloseLevel=InpMACDCloseLevel;
  ExtMATrendPeriod=InpMATrendPeriod;
class=class="str">"cmt">//--- 创建所有需要的对象
  if(!ExtExpert.Init())
    class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 创建程序对话框
  if(!ExtDialog.Create(class="num">0,"Notification",class="num">0,class="num">100,class="num">100,class="num">360,class="num">380))
    class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 运行程序
  if(!ExtDialog.Run())
    class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 成功
  class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//--- 成功
  class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA反初始化函数
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
class=class="str">"cmt">//--- 
  Comment("");
class=class="str">"cmt">//--- 销毁对话框
  ExtDialog.Destroy(reason);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA中处理新报价到来的函数
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick(class="type">void)
class=class="str">"cmt">//--- 如果EA执行则改变超时的限制时间(秒)
        if(ExtExpert.Processing())

「把图表事件接进控件对话框」

EA 面板要能响应鼠标点击和键盘输入,靠的是 OnChartEvent 把 MT5 图表事件转发给自定义对话框类。下面这段代码只做一件事:收到任意图表事件,立刻交给 ExtDialog.ChartEvent 处理,控件上的按钮状态才会实时刷新。 void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ExtDialog.ChartEvent(id,lparam,dparam,sparam); } 类声明里 OnEvent 是虚函数,意味着子类可以重写事件逻辑;Initialization 则把邮件、推送、警报开关以及 Lots、TakeProfit、TrailingStop、MACDOpenLevel、MACDCloseLevel、MATrendPeriod 这些实盘参数一次性灌进对象。 bool CControlsDialog::Initialization(const bool Mail,const bool Push,const bool Alert_, const double Lots,const int TakeProfit, const int TrailingStop,const int MACDOpenLevel, const int MACDCloseLevel,const int MATrendPeriod) { mMail=Mail; mPush=Push; mAlert_=Alert_; mLots=Lots; mTakeProfit=TakeProfit; mTrailingStop=TrailingStop; 在 MT5 里开个测试 EA,把这组初始化参数接上你自己的面板类,改一行 MACDOpenLevel 从 3 调到 5,回测里触发开仓的次数可能明显减少——外汇和贵金属波动大,这类参数微调只代表概率变化,实盘前务必用策略测试器跑够样本。

MQL5 / C++
limit_time=TimeCurrent()+ExtTimeOut;
}
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| ChartEvent函数                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnChartEvent(class="kw">const class="type">int id,
                  class="kw">const class="type">long &lparam,
                  class="kw">const class="type">class="kw">double &dparam,
                  class="kw">const class="type">class="kw">string &sparam)
  {
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">virtual class="type">bool      OnEvent(class="kw">const class="type">int id,class="kw">const class="type">long &lparam,class="kw">const class="type">class="kw">double &dparam,class="kw">const class="type">class="kw">string &sparam);
                  class=class="str">"cmt">//--- 初始化
   class="kw">virtual class="type">bool      Initialization(class="kw">const class="type">bool Mail,class="kw">const class="type">bool Push,class="kw">const class="type">bool Alert_,
                                     class="kw">const class="type">class="kw">double Lots,class="kw">const class="type">int TakeProfit,
                                     class="kw">const class="type">int   TrailingStop,class="kw">const class="type">int MACDOpenLevel,
                                     class="kw">const class="type">int   MACDCloseLevel,class="kw">const class="type">int MATrendPeriod);
class="kw">protected:
   class=class="str">"cmt">//--- 创建独立控件
   class="type">bool                CreateCheckGroup(class="type">void);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 初始化    
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CControlsDialog::Initialization(class="kw">const class="type">bool Mail,class="kw">const class="type">bool Push,class="kw">const class="type">bool Alert_,
                                     class="kw">const class="type">class="kw">double Lots,class="kw">const class="type">int TakeProfit,
                                     class="kw">const class="type">int   TrailingStop,class="kw">const class="type">int MACDOpenLevel,
                                     class="kw">const class="type">int   MACDCloseLevel,class="kw">const class="type">int MATrendPeriod)
  {
   mMail=Mail;
   mPush=Push;
   mAlert_=Alert_;
   mLots=Lots;
   mTakeProfit=TakeProfit;
   mTrailingStop=TrailingStop;
让小布替你跑这套
这些面板模板和小布盯盘里的AIGC诊断已内置,打开对应品种页即可看到参数响应结构,你只需专注逻辑而非重复搭界面。

常见问题

首次调用时prev_time为空值,需显式初始化为time[0],否则首 tick 可能误触发,建议在函数入口做空值判断。
目前小布提供参数响应结构示例与诊断,完整面板代码仍需在MQL5编辑器中按本文方式拼接,但重复劳动可交给小布预览。
需确保对象前缀唯一,避免图表上控件ID冲突,建议在Create时拼接程序类型与符号名。
多数面板靠定时器或事件轮询读输入,若未挂OnChartEvent或Timer,参数变更只停留在界面层,不会回写程序变量。