向指标或者EA中快速添加控制面板·综合运用
🎛️

向指标或者EA中快速添加控制面板·综合运用

(3/3)· 从指标检测到图形面板联动,手把手把免重启调参真正跑通在实盘程序里

新手友好 第 3/3 篇
很多交易者写了能用的EA却懒得改参数,因为每次调整都要重载程序、断掉运行状态。其实基于标准类库挂一个轻量面板,就能在运行中直接拨动设置。这篇把前面两篇的零件拼成完整可用的一套。

参数回读:对话框怎么把控件值吐给EA

在 MT5 的面板类里,Initialization 负责把外部传入的 Lots、TakeProfit、MACDOpenLevel 等 9 个参数写进成员变量;末尾三行 mMACDOpenLevel=MACDOpenLevel; mMACDCloseLevel=MACDCloseLevel; mMATrendPeriod=MATrendPeriod; 就是典型落库动作,return(true) 表示初始化成功。 真正让 EA 后续能读到用户改过的值,靠的是 GetValues 这套引用传参。它把 mMail、mLots、mMACDOpenLevel 等成员逐一赋给入参引用,调用方拿到的是实时内存值,不是初始化时的快照。 控件文本回写也有坑:m_edit1.Text("Edit1") 失败会直接中断流程;而手数显示用 DoubleToString(mLots,2) 固定保留两位小数,意味着 0.1 手会显示成 '0.10'。开 MT5 把这段塞进你的 CControlsDialog,改个 Lots 看编辑框是否同步,就能验证回读链路通没通。外汇贵金属面板参数误读可能引发异常下单,属高风险操作。

MQL5 / C++
mMACDOpenLevel=MACDOpenLevel;
mMACDCloseLevel=MACDCloseLevel;
mMATrendPeriod=MATrendPeriod;
class=class="str">"cmt">//---
class="kw">return(true);
}

class="type">int CControlsDialog::GetCheck(class="kw">const class="type">int idx)

if(!m_edit1.Text("Edit1"))

if(!m_edit1.Text(DoubleToString(mLots,class="num">2)))

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=class="str">"cmt">//--- 获取值
class="kw">virtual class="type">void GetValues(class="type">bool &Mail,class="type">bool &Push,class="type">bool &Alert_,
                       class="type">class="kw">double &Lots,class="type">int &TakeProfit,
                       class="type">int &TrailingStop,class="type">int &MACDOpenLevel,
                       class="type">int &MACDCloseLevel,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">void CControlsDialog::GetValues(class="type">bool &Mail,class="type">bool &Push,class="type">bool &Alert_,
                                class="type">class="kw">double &Lots,class="type">int &TakeProfit,
                                class="type">int &TrailingStop,class="type">int &MACDOpenLevel,
                                class="type">int &MACDCloseLevel,class="type">int &MATrendPeriod)
  {
  Mail=mMail;
  Push=mPush;
  Alert_=mAlert_;
  Lots=mLots;
  TakeProfit=mTakeProfit;
  TrailingStop=mTrailingStop;
  MACDOpenLevel=mMACDOpenLevel;
  MACDCloseLevel=mMACDCloseLevel;
  MATrendPeriod=mMATrendPeriod;
  }

「把勾选状态变成邮件推送和弹窗」

在 MT5 的自定义面板类里,通知发送不是直接写死,而是靠复选框索引驱动。CControlsDialog::Notifications 这个函数就是入口:先拿 m_check_group.ControlsTotal() 得到组里控件数,再按索引 0、1、2 分别判断要不要发邮件、推手机通知、弹终端 Alert。

MQL5 / C++
class="type">void CControlsDialog::Notifications(class="kw">const class="type">class="kw">string text)
  {
   class="type">int i=m_check_group.ControlsTotal();
   if(GetCheck(class="num">0))
      SendMail(" ",text);
   if(GetCheck(class="num">1))
      SendNotification(text);
   if(GetCheck(class="num">2))
      Alert(text);
  }
上面这段逐行拆:第 3 行 i 存了控件总数(实际发送逻辑没用到 i,只是顺手取了);GetCheck(0) 为真就调 SendMail 发邮件,标题留空格;GetCheck(1) 为真走 SendNotification 推送到绑定的移动端;GetCheck(2) 为真用 Alert 在终端弹窗。三者独立,可同时触发。 OnClickButtonOK 里另有变更检测:把 m_check_group.Check(0/1/2) 和旧值 mMail/mPush/mAlert_ 比对,只要有一个不等就把 mModification 置 true,说明用户改了通知配置。外汇和贵金属行情跳空频繁,这类通知开关最好按品种分开存,避免非交易时段被刷屏。 实盘前开 MT5 自建个带 3 个复选框的 dialog,把这段直接塞进去,勾不同组合看终端和手机谁响,就能验证索引映射没错。

MQL5 / C++
class="type">void CControlsDialog::Notifications(class="kw">const class="type">class="kw">string text)
  {
   class="type">int i=m_check_group.ControlsTotal();
   if(GetCheck(class="num">0))
      SendMail(" ",text);
   if(GetCheck(class="num">1))
      SendNotification(text);
   if(GetCheck(class="num">2))
      Alert(text);
  }

◍ 面板改动如何触发EA重算

这段逻辑来自一个 MACD 样条 EA 的参数面板响应函数。每当交易者在对话框里改了手数、止盈、追踪止损、MACD 开仓/平仓阈值或均线趋势周期,程序就把对应编辑框文本转成数值,并与内存里的旧值比对;不一致就覆盖变量并把 mModification 置真,后续 tick 才会按新参数重算信号。 具体看几个关键比对:m_edit1 控制 Lots,m_edit2 管 TakeProfit,m_edit3 管 TrailingStop,m_edit4/m_edit5 分别是 MACDOpenLevel 与 MACDCloseLevel,m_edit6 对应 MATrendPeriod。全部用 StringToInteger(StringToDouble(...)) 做强制转换,意味着编辑框里填 "1.2" 也会被截成整型 1,外汇与贵金属品种上设小数手数会直接失效,属于高风险下的隐性坑。 初始化阶段还有两个硬参数:ExtTimeOut=10 表示两次交易操作之间至少隔 10 秒;bool_tester 默认 false,若 MQLInfoInteger(MQL_TESTER) 返回假才走实盘分支,并创建 360x380 的 Notification 对话框。你在 MT5 里把这段挂上 EURUSD 回测,改面板数值后若信号没变,先查 mModification 有没有被置位。

MQL5 / C++
if(StringToInteger(m_edit2.Text())!=mTakeProfit)
  {
   mTakeProfit=(class="type">int)StringToDouble(m_edit2.Text());
   mModification=true;
  }
 if(StringToInteger(m_edit3.Text())!=mTrailingStop)
  {
   mTrailingStop=(class="type">int)StringToDouble(m_edit3.Text());
   mModification=true;
  }
 if(StringToInteger(m_edit4.Text())!=mMACDOpenLevel)
  {
   mMACDOpenLevel=(class="type">int)StringToDouble(m_edit4.Text());
   mModification=true;
  }
 if(StringToInteger(m_edit5.Text())!=mMACDCloseLevel)
  {
   mMACDCloseLevel=(class="type">int)StringToDouble(m_edit5.Text());
   mModification=true;
  }
 if(StringToInteger(m_edit6.Text())!=mMATrendPeriod)
  {
   mMATrendPeriod=(class="type">int)StringToDouble(m_edit6.Text());
   mModification=true;
  }
class="type">int ExtTimeOut=class="num">10; class=class="str">"cmt">// 交易操作之间的间隔时间(秒)
class="type">bool bool_tester=class="kw">false; class=class="str">"cmt">// true - 测试器模式
if(!MQLInfoInteger(MQL_TESTER))
  {
   bool_tester=class="kw">false;
   ExtDialog.Initialization(ExtMail,ExtPush,ExtAlert,
            ExtLots,ExtTakeProfit,ExtTrailingStop,
            ExtMACDOpenLevel,ExtMACDCloseLevel,ExtMATrendPeriod);
   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);
  }

面板改参后如何热重载EA逻辑

在MT5里给EA挂一个交互面板,最怕的是改了手数、止盈或MACD阈值后策略不生效,只能重启终端。上面的片段给出了一种不重启就能热重载的做法:图表事件触发后先问面板「参数变没变」,变了就拉新值并重新初始化。 核心判断在 ExtDialog.Modification()。它返回true代表用户在面板上动过输入项;此时用 GetValues() 把邮件开关、手数、止盈、 trailing stop、MACD开平电平、均线趋势周期一次性读回。随后调 ExtExpert.Init() 重建内部状态,成功才把 Modification 标记复位并打印新参数,失败则打印 Parameter change error。 实盘里这套机制的价值是:你可以在欧美盘切换时把 ExtLots 从0.1调到0.3、把 ExtMATrendPeriod 从12改成18,EA下一 tick 就按新参数跑,不用断开持仓。外汇与贵金属杠杆高,参数热改若没做 Init 校验,可能出现旧仓位和新逻辑错位,建议先在策略测试器里用历史数据验证 Modification 分支。 让小布替你跑这套 把上面代码段直接贴进你的EA的 OnChartEvent,配合一个 CDialog 派生面板,就能在MT5里手动拖参不重启。重点盯 Print 那行输出,确认 Lots 和 MATrendPeriod 真的随面板变动。

MQL5 / C++
class=class="str">"cmt">//--- 运行程序
if(!ExtDialog.Run())
   class="kw">return(INIT_FAILED);
   }
 else
   bool_tester=true;
class=class="str">"cmt">//--- 成功
 class="kw">return(INIT_SUCCEEDED);
 }
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">// 查询面板中的bool变量,参数是否已经改变
class=class="str">"cmt">// 如果是,查询面板参数并调用
class=class="str">"cmt">// CSampleExpert::Init(class="type">void)
  if(ExtDialog.Modification())
    {
    ExtDialog.GetValues(ExtMail,ExtPush,ExtAlert,
                        ExtLots,ExtTakeProfit,ExtTrailingStop,
                        ExtMACDOpenLevel,ExtMACDCloseLevel,ExtMATrendPeriod);
    if(ExtExpert.Init())
      {
       ExtDialog.Modification(class="kw">false);
       Print("Parameters changed, ",ExtLots,", ",ExtTakeProfit,", ",ExtTrailingStop,", ",
             ExtMACDOpenLevel,", ",ExtMACDCloseLevel,", ",ExtMATrendPeriod);
      }
    else
      {
       ExtDialog.Modification(class="kw">false);
       Print("Parameter change error");
      }
    }
  }

「记住这一条就够了」

把面板塞进指标只是体力活:面板类里把尺寸、坐标和事件响应都封装好,指标里挂一个 OnChartEvent() 就能跑。真正卡人的是 EA 与面板之间的通信——如果面板从第一行起就没预留集成接口,后期接 EA 基本要重写。 文末给的七个文件就是活样板:NewBarOriginal.mq5 到 NewBar.mq5 是指标改造对照,PanelDialog 系列是面板从裸写到可通信的演进,MACD Sample.mq5 则是接好面板后的 EA 成品。下载包里 newbar.mq5 占 3.24 KB、paneldialog2.mqh 有 28.48 KB,直接拖进 MT5 的 MQL5 目录就能编译验证。 下面这段改色代码演示了“先 undef 再 define 再 include”的顺序铁律——顺序错了标准库颜色就覆盖不上去。 外汇与贵金属挂 EA 面板属于高风险操作,回测通过不代表实盘不滑点爆仓,通信逻辑建议先在策略测试器跑满一周 tick 再上真仓。

MQL5 / C++
class=class="str">"cmt">// BEFORE connecting Dialog.mqh!
class="macro">#include <Controls\Defines.mqh>
class=class="str">"cmt">//--- 忘记旧颜色
class="macro">#undef   CONTROLS_DIALOG_COLOR_BORDER_LIGHT
class="macro">#undef   CONTROLS_DIALOG_COLOR_BORDER_DARK
class="macro">#undef   CONTROLS_DIALOG_COLOR_BG
class="macro">#undef   CONTROLS_DIALOG_COLOR_CAPTION_TEXT
class="macro">#undef   CONTROLS_DIALOG_COLOR_CLIENT_BG
class="macro">#undef   CONTROLS_DIALOG_COLOR_CLIENT_BORDER
class=class="str">"cmt">//--- 设置新颜色
class="macro">#define CONTROLS_DIALOG_COLOR_BORDER_LIGHT   clrWhite             class=class="str">"cmt">// 对话框边框颜色(外部)
class="macro">#define CONTROLS_DIALOG_COLOR_BORDER_DARK   C&class="macro">#x27;0xB6,0xB6,0xB6&class="macro">#x27;   class=class="str">"cmt">// 对话框边框颜色(内部)
class="macro">#define CONTROLS_DIALOG_COLOR_BG             clrLightGreen       class=class="str">"cmt">// 对话框背景(标题下方和客户端区域周围)
class="macro">#define CONTROLS_DIALOG_COLOR_CAPTION_TEXT   C&class="macro">#x27;0x28,0x29,0x3B&class="macro">#x27;   class=class="str">"cmt">// 对话框标题文字颜色
class="macro">#define CONTROLS_DIALOG_COLOR_CLIENT_BG      clrAliceBlue        class=class="str">"cmt">// 客户区域的背景颜色
class="macro">#define CONTROLS_DIALOG_COLOR_CLIENT_BORDER C&class="macro">#x27;0xC8,0xC8,0xC8&class="macro">#x27;   class=class="str">"cmt">// 客户区域的颜色
class=class="str">"cmt">// 现在连接
class="macro">#include <Controls\Dialog.mqh>
让小布替你跑这套
这些面板联动与参数热读的逻辑,小布盯盘的AIGC辅助已内置了常用模板,打开对应品种页即可套用现成结构,你只改业务判断部分。

常见问题

概率上在极密tick或历史数据不连续时会有边界偏差,建议同时用CopyTime做交叉校验,而非只依赖单变量比较。
在面板事件里写回全局变量或配置文件,OnTimer或OnChartEvent轮询读取,发现变动就刷新内部状态。
可以,小布盯盘的AIGC模块提供面板骨架代码,你补上指标逻辑就能编译出可热调参的程序。
多数只做了界面没接参数回写,真正好用的是把控件状态和程序输入参数双向绑定,这部分往往被省略。
关于标准类库CAppDialog的进阶美化见同系列前两篇的基础拆解,本篇侧重把链路跑通。