从用户面板"动态"更改"EA 交易"参数·综合运用
(3/3)· 当 EA 外部参数堆到几十个,手动改设置比盯盘还累,这篇给完整落地方案
面板编辑框里的输入校验与回写
在 MT5 自定义指标面板里,用户改了编辑框内容并不会自动生效,必须靠 CHARTEVENT_OBJECT_ENDEDIT(id==3)事件捕获。上面这段代码先拿 sparam 里的对象名,只处理名为 "editValIP" 的编辑框,其余控件忽略。 当 number==0 即修改「指标周期」时,代码做了硬约束:如果输入是 "0"、空串、或 SD() 解析出的数值 ≤0,就强制把 currVal 改写成 "1"。这能避免把周期设成非法值导致指标句柄失效。 校验通过后,用 ObjectSetString 把值写回面板上的 "enumMB0" 文本对象,完成界面与内部变量的同步。外汇与贵金属波动剧烈,这类输入防护能降低 EA 在极端行情下因参数异常崩掉的概率,建议直接复制这段逻辑到自己的面板回调里验证。
class="type">bool ChartEvent_ObjectEndEdit(class="type">int id,class="type">long lparam,class="type">class="kw">double dparam,class="type">class="kw">string sparam) { if(id==CHARTEVENT_OBJECT_ENDEDIT) class=class="str">"cmt">// id==class="num">3 { class="type">class="kw">string editObject=sparam; class=class="str">"cmt">// Name of the edited object class=class="str">"cmt">//--- class=class="str">"cmt">// If the value has been entered in the input box in the dialog window if(editObject=="editValIP") { class=class="str">"cmt">// Get the entered value currVal=ObjectGetString(class="num">0,"editValIP",OBJPROP_TEXT); class=class="str">"cmt">//--- class=class="str">"cmt">// (class="num">0) Period Indicator if(number==class="num">0) { class=class="str">"cmt">// Correct the value if it is wrong if(currVal=="class="num">0" || currVal=="" || SD(currVal)<=class="num">0) { currVal="class="num">1"; } class=class="str">"cmt">//--- class=class="str">"cmt">// Set the entered value ObjectSetString(class="num">0,"enumMB0",OBJPROP_TEXT,currVal); } class=class="str">"cmt">//--- class=class="str">"cmt">// (class="num">4) Lot
◍ 输入框校验与面板回写逻辑
这段处理的是 MT5 面板里手填参数后的兜底:当编号 number 为 4(对应成交量类字段)时,若当前字符串 currVal 是 "0"、空串,或经 SD() 转double后小于等于 0,就把它重置为 SS.vol_min 保留两位小数的值,避免零手数发单。 编号 1 和 2 分别对应止盈、止损点数,逻辑更硬:只要填了非法值(0、空、或转数值≤0),直接回退成 "1" 点,保证风控字段不可能静默失效。 回写用 ObjectSetString 打到 enumMB0 / enumMB1 文本对象,最后删掉临时编辑框 editValIP 并 ChartRedraw(),界面即时刷新。外汇与贵金属波动剧烈,这类输入校验缺失可能在跳空时放大滑点风险,建议直接把这段拷进自己的 EA 面板事件分支里跑一遍。
if(number==class="num">4) { class=class="str">"cmt">// Correct the value if it is wrong if(currVal=="class="num">0" || currVal=="" || SD(currVal)<=class="num">0) { currVal=DS(SS.vol_min,class="num">2); } class=class="str">"cmt">//--- class=class="str">"cmt">// Set the entered value ObjectSetString(class="num">0,"enumMB0",OBJPROP_TEXT,DS2(SD(currVal))); } class=class="str">"cmt">//--- class=class="str">"cmt">// (class="num">1) Take Profit(p) class=class="str">"cmt">// (class="num">2) Stop Loss(p) if(number==class="num">1 || number==class="num">2) { class=class="str">"cmt">// Correct the value if it is wrong if(currVal=="class="num">0" || currVal=="" || SD(currVal)<=class="num">0) { currVal="class="num">1"; } class=class="str">"cmt">//--- class=class="str">"cmt">// Set the entered value ObjectSetString(class="num">0,"enumMB1",OBJPROP_TEXT,currVal); } class=class="str">"cmt">//--- DelObjbyName("editValIP"); ChartRedraw(); } } class=class="str">"cmt">//--- class="kw">return(false); }
「别急着下结论」
随附的压缩包(约 26.38 KB)里放了可跑的面板样例,想深究直接拖进 MT5 的 MQL5 目录解压验证。原文作者故意漏了两类边界检查:对话框弹出后若手动改图表宽高,面板不会自动回中;列表切换选项时,标记选中行的对象会错位——这两处留给你当练手。 下面这段是评论区一位用户踩过的坑:用变量 tf / tf1 传给 iCustom 想「即时」切周期却失效,写死 PERIOD_M5、PERIOD_M15 反而正常。 ENUM_TIMEFRAMES tf0,tf,tf1; void OnInit() { ................. tf0 = PERIOD_CURRENT; switch (PERIOD_CURRENT) { case PERIOD_M1: tf =PERIOD_M5;tf1 =PERIOD_M15; break; case PERIOD_M5: tf =PERIOD_M15; tf1 =PERIOD_H1;break; case PERIOD_M15: tf =PERIOD_H1;tf1 =PERIOD_H4;break; case PERIOD_M30: tf =PERIOD_H2;tf1 =PERIOD_H8; break; case PERIOD_H1: tf =PERIOD_H4; tf1 =PERIOD_H12;break; case PERIOD_H4: tf =PERIOD_H12; tf1 =PERIOD_D1; break; case PERIOD_D1: tf =PERIOD_D1; tf1 =PERIOD_W1; break; } //--- 获取 MA 的手柄 Ext1Handle=iCustom(NULL,PERIOD_CURRENT,"xxxx",SlowEMA1);//工作正常 Ext2Handle=iCustom(NULL,PERIOD_M5,"xxxx",SlowEMA1);//工作正常 Ext3Handle=iCustom(NULL,PERIOD_M15,"xxxx",SlowEMA1);//工作正常 //下面的工作出错! // Ext1Handle=iCustom(NULL,PERIOD_CURRENT, "xxxx",SlowEMA1); // Ext2Handle=iCustom(NULL,tf, "xxxx",SlowEMA1); // Ext3Handle=iCustom(NULL,tf1, "xxxx",SlowEMA1); 逐行看:tf0/tf/tf1 声明周期枚举变量;OnInit 里先把 tf0 设为当前周期,再用 switch 按当前周期给 tf、tf1 派生出更高层级周期。Ext1~3 用写死周期调用 iCustom 能拿到句柄,但换成变量 tf、tf1 的注释行却报错——说明 OnInit 阶段变量周期未必被终端接受,可能要在别处刷新句柄。 外汇和贵金属波动剧烈、杠杆风险高,这类 GUI 练习只帮你熟悉 MQL5 机制,别直接当实盘信号。把上面代码贴进 MT5 试一次,比看十遍文字更有用。
ENUM_TIMEFRAMES tf0,tf,tf1; class="type">void OnInit() { ................. tf0 = PERIOD_CURRENT; class="kw">switch (PERIOD_CURRENT) { case PERIOD_M1: tf =PERIOD_M5;tf1 =PERIOD_M15; break; case PERIOD_M5: tf =PERIOD_M15; tf1 =PERIOD_H1;break; case PERIOD_M15: tf =PERIOD_H1;tf1 =PERIOD_H4;break; case PERIOD_M30: tf =PERIOD_H2;tf1 =PERIOD_H8; break; case PERIOD_H1: tf =PERIOD_H4; tf1 =PERIOD_H12;break; case PERIOD_H4: tf =PERIOD_H12; tf1 =PERIOD_D1; break; case PERIOD_D1: tf =PERIOD_D1; tf1 =PERIOD_W1; break; } class=class="str">"cmt">//--- 获取 MA 的手柄 Ext1Handle=iCustom(NULL,PERIOD_CURRENT,"xxxx",SlowEMA1);class=class="str">"cmt">//工作正常 Ext2Handle=iCustom(NULL,PERIOD_M5,"xxxx",SlowEMA1);class=class="str">"cmt">//工作正常 Ext3Handle=iCustom(NULL,PERIOD_M15,"xxxx",SlowEMA1);class=class="str">"cmt">//工作正常 class=class="str">"cmt">//下面的工作出错! class=class="str">"cmt">// Ext1Handle=iCustom(NULL,PERIOD_CURRENT, "xxxx",SlowEMA1); class=class="str">"cmt">// Ext2Handle=iCustom(NULL,tf, "xxxx",SlowEMA1); class=class="str">"cmt">// Ext3Handle=iCustom(NULL,tf1, "xxxx",SlowEMA1);