构建自动运行的 EA(第 09 部分):自动化(II)·综合运用
「按交易日切分挂单时间窗」
这套 EA 的输入参数把一周七天各自的开收时间写成字符串,周日是 00:00-00:00 代表全天不交易,周一给到 09:05-17:35,周五则是 12:55-16:25,等于把外汇周内的流动性高峰用硬编码框死了。 颜色与保本点也走输入口:user06 取 clrForestGreen 画 Take 线,user07 取 clrFireBrick 画 Stop 线,user08 设 35 作为 BreakEven 的财务阈值(单位依品种点值而定,贵金属与外汇杠杆不同需自测)。 OnInit 里先 new 出管理器和鼠标对象,随后用 ENUM_DAY_OF_WEEK 从 SUNDAY 扫到 SATURDAY,switch 把 user90~user96 逐一塞进 szInfo。这段循环就是时间窗路由的核心,改掉某个 user9x 字符串就能单独挪那一天的交易时段。 实盘前务必在 MT5 策略测试器里把周一时段改成你经纪商的真实开盘,否则可能整周漏单或撞上跳空。外汇与贵金属杠杆高,参数错配会放大滑点风险。
input class="type">class="kw">color user06 = clrForestGreen; class=class="str">"cmt">//Take Line Color input class="type">class="kw">color user07 = clrFireBrick; class=class="str">"cmt">//Stop Line Color input class="type">class="kw">double user08 = class="num">35; class=class="str">"cmt">//BreakEven( FINANCE ) class=class="str">"cmt">//+------------------------------------------------------------------+ input class="type">class="kw">string user90 = "class="num">00:class="num">00 - class="num">00:class="num">00"; class=class="str">"cmt">//Sunday input class="type">class="kw">string user91 = "class="num">09:class="num">05 - class="num">17:class="num">35"; class=class="str">"cmt">//Monday input class="type">class="kw">string user92 = "class="num">10:class="num">05 - class="num">16:class="num">50"; class=class="str">"cmt">//Tuesday input class="type">class="kw">string user93 = "class="num">09:class="num">45 - class="num">13:class="num">38"; class=class="str">"cmt">//Wednesday input class="type">class="kw">string user94 = "class="num">11:class="num">07 - class="num">15:class="num">00"; class=class="str">"cmt">//Thursday input class="type">class="kw">string user95 = "class="num">12:class="num">55 - class="num">16:class="num">25"; class=class="str">"cmt">//Friday input class="type">class="kw">string user96 = "class="num">00:class="num">00 - class="num">00:class="num">00"; class=class="str">"cmt">//Saturday class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define def_MAGIC_NUMBER class="num">987654321 class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class="type">class="kw">string szInfo; manager = new C_Manager(def_MAGIC_NUMBER, user03, user02, user01, user04, user08); mouse = new C_Mouse(user05, user06, user07, user03, user02, user01); for (ENUM_DAY_OF_WEEK c0 = SUNDAY; c0 <= SATURDAY; c0++) { class="kw">switch (c0) { case SUNDAY : szInfo = user90; class="kw">break; case MONDAY : szInfo = user91; class="kw">break; case TUESDAY : szInfo = user92; class="kw">break; case WEDNESDAY : szInfo = user93; class="kw">break; case THURSDAY : szInfo = user94; class="kw">break;
用毫秒定时器刷交易日提示
这段 MQL5 片段承接前面初始化逻辑,按系统返回的星期枚举把 user95、user96 这类外部文本塞进面板控件。FRIDAY 对应 user95,SATURDAY 对应 user96,最后由 (*manager).SetInfoCtrl(c0, szInfo) 写到编号 c0 的信息区,等于让 EA 在周五、周六显示不同的运维话术。 真正驱动界面活起来的不是 OnInit 本身,而是 EventSetMillisecondTimer(100) 这行——它把定时器精度压到 100 毫秒,配合 CheckToleranceLevel() 做容差校验,使控件刷新几乎无感延迟。外汇与贵金属市场周末休市,这种按星期切换的提示在实盘里能帮助快速识别非交易时段,但杠杆品种波动剧烈,任何面板逻辑都不能替代风控。 直接把上面代码粘进 MT5 的 EA 模板,改 user95/user96 成你自己的文案,再确认 c0 已在 CreateInfoCtrl 里注册,编译后挂 EURUSD 或 XAUUSD 观察周五切换是否准时。
case FRIDAY : szInfo = user95; class="kw">break; case SATURDAY : szInfo = user96; class="kw">break; } (*manager).SetInfoCtrl(c0, szInfo); } (*manager).CheckToleranceLevel(); EventSetMillisecondTimer(class="num">100); class="kw">return INIT_SUCCEEDED;
◍ 输入时间越界会被静默纠正
前面做的 EA 时间控制逻辑很简单:只允许在指定时段内运行。但有个边界细节容易被忽略——若交互界面里填的结束时间大于 24 小时,系统会把它硬拉回最接近 24 小时的值。 比如做外汇时你想让 EA 跑到 22:59,就得精确敲入;若手滑写成 25:59,代码会自动改成 23:59。这种键入错误虽不常发生,但一旦踩中,运行窗口就偏移了将近一小时。 原作者没在初版加越界检查,因为概率低;但附件 ZIP(EA_Automatico_-_10.zip,10.75 KB)里的修订版补上了:新增变量并拆分时间数据,确认输入值不超过单日最大 86400 秒。 本地时间还是服务器时间?默认填本地时间,想对齐图表服务器时间只需改代码里一行。外汇和贵金属波动剧烈、杠杆高风险大,时间窗口错配可能让策略在错误时段成交。 下面这段是修正版里拆分与取模的核心,注意 StringToTime 结果对 86400 取余,就是把跨日时间折回当日秒数:
<span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">void</span> SetInfoCtrl(<span class="keyword">class="kw">const</span> <span class="macro">ENUM_DAY_OF_WEEK</span> index, <span class="keyword">class="kw">const</span> <span class="keyword">class="type">class="kw">string</span> szArg) <span class="keyword">final</span> { <span class="keyword">class="type">class="kw">string</span> szRes[]<span style="background-class="type">class="kw">color:rgb(class="num">177, class="num">210, class="num">143);">, sz1[]</span>; <span class="keyword">class="type">bool</span> bLocal; <span class="keyword">if</span> (<span class="predefines">_LastError</span> != <span class="macro">ERR_SUCCESS</span>) <span class="keyword">class="kw">return</span>; <span class="keyword">if</span> ((index > <span class="macro">SATURDAY</span>) || (index < <span class="macro">SUNDAY</span>)) <span class="keyword">class="kw">return</span>; <span class="keyword">if</span> (bLocal = (<span class="functions">StringSplit</span>(szArg, <span class="class="type">class="kw">string">&class="macro">#x27;-&class="macro">#x27;</span>, szRes) == <span class="number">class="num">2</span>)) { m_InfoCtrl[index].Init = (<span class="functions">StringToTime</span>(szRes[<span class="number">class="num">0</span>]) % <span class="number">class="num">86400</span>); m_InfoCtrl[index].End = (<span class="functions">StringToTime</span>(szRes[<span class="number">class="num">1</span>]) % <span class="number">class="num">86400</span>);
「别急着下结论」
上面这段逻辑干的事,是先判断时段起止是否合理,再把字符串按冒号拆成时、分两段做范围校验:小时不超过 23、分钟不超过 59,任一不满足就打印错误并卸载 EA。 bLocal 初值来自 m_InfoCtrl[index].Init <= m_InfoCtrl[index].End,循环里 c0 只在 0 和 1 之间跑,且只要 bLocal 变 false 就立刻停。StringSplit 返回值不等于 2 也会直接判废,说明格式必须严丝合缝是「时:分」。 外汇和贵金属杠杆高、滑点随机,这类时间解析若在生产 EA 里写错,可能让策略在错误时段开仓。把这段代码贴进 MT5 的 OnInit 段跑一遍,故意传个「25:00」进去,看 ExpertRemove 是否被触发,比看文档来得实在。
bLocal = (m_InfoCtrl[index].Init <= m_InfoCtrl[index].End); for (class="type">char c0 = class="num">0; (c0 <= class="num">1) && (bLocal); c0++) if (bLocal = (StringSplit(szRes[class="num">0], &class="macro">#x27;:&class="macro">#x27;, sz1) == class="num">2)) bLocal = (StringToInteger(sz1[class="num">0]) <= class="num">23) && (StringToInteger(sz1[class="num">1]) <= class="num">59); if (_LastError == ERR_WRONG_STRING_DATE) ResetLastError(); if ((_LastError != ERR_SUCCESS) || (!bLocal)) { Print("Error in the declaration of the time of day: ", EnumToString(index)); ExpertRemove(); }