MT4账户无效 - MT4周线收盘前自动平仓全订单条件设置详解

搞懂MT4服务器时间与周末收盘的具体节点
在写任何跟时间相关的EA逻辑之前,必须先搞清楚MT4的服务器时间是怎么走的。MT4平台左上角显示的时间通常是服务器所在时区的时间,绝大多数外汇经纪商用的都是格林尼治标准时间加2或者加3,也就是东欧时间。但不同平台可能稍有差异,有的用纽约时间,有的用悉尼时间,所以第一步要做的不是急着写代码,而是打开MT4的“市场报价”窗口,看看服务器时间跟你本地时间差了多久。
关于每周收盘的具体时间点,外汇市场通常是在北京时间周六凌晨四五点左右收盘,对应服务器时间就是周五晚上的22点或23点、24点不等,具体取决于经纪商的服务器时区设定。比如常见的服务器时间GMT+3,那周五收盘就是服务器时间24点整,也就是周六0点。如果服务器是GMT+2,收盘就是23点。这里有个很容易踩的坑,就是不能直接拿北京时间去写条件,必须换算成服务器时间。
还有个细节值得注意,很多经纪商会在周五收盘前提前几分钟停止接受新订单或者关闭交易服务器,有的平台在收盘前五分钟就不允许开新仓了,但平仓操作通常还能执行。所以EA的平仓触发时间不应该设置在收盘前一秒,最好提前十分钟到半小时,给平仓操作留出足够的缓冲余量,避免因为网络延迟或者服务器卡顿导致平仓指令没发出去。
我个人建议的设置思路是,把平仓时间设置在服务器时间周五的22点30分到23点之间,具体数值根据自己的平台来定。如果你不确定服务器时间,可以先手动观察一周,记录一下周五价格停止跳动的准确时间,然后往前推十五分钟作为EA的触发点,这样最稳妥。
EA里用星期几和小时分钟组合判断平仓条件
MQL4语言里判断星期和时间其实非常简单,用内置函数TimeDayOfWeek()可以获取当前服务器时间的星期几,返回的数字1到5分别对应周一到周五,0和6对应周六周日。用Hour()和Minute()函数可以分别拿到当前服务器时间的小时数和分钟数。有了这三个函数,组合判断条件就很容易写了。
比如你想让EA在周五的22点45分开始执行平仓,判断逻辑就可以写成:if(TimeDayOfWeek()==5 && Hour()==22 && Minute()>=45)。这个条件的意思是,当服务器时间是周五,并且小时等于22,分钟大于等于45的时候,条件成立。当然分钟的条件写成>=45会比==45更安全,因为EA的计时器触发时机不一定精确到每一秒,万一正好错过了45分整这个点,用==就永远触发不了。
还有一种更保守的写法,就是设定一个时间范围,比如判断是否在周五的22点30分到23点30分之间,这样哪怕EA因为某些原因延迟了几分钟触发,只要还在这个时间窗口内,平仓逻辑依然会执行。这种范围判断比精确到某个分钟更实用,因为MT4的EA并不是每秒钟都在运行的,它依赖于图表上的价格变动来触发Tick事件,如果那个时间段价格波动很小,触发频率就会降低。
这里要特别提醒一下,很多新手会忽略ChartID和订单所属图表的问题。如果你的EA只挂在某一个货币对的图表上,那它只能平掉这个图表相关的订单吗?其实不是的,EA完全可以通过遍历所有持仓订单来平掉整个账户的所有单子,关键是代码里要用OrdersTotal()函数循环扫描所有订单,而不是用OrderSelect()去选中特定图表的单子。这样不管订单是哪个品种、哪个图表创建的,都能被平掉。
完整平仓逻辑代码的编写思路与关键函数
写平仓功能的核心就是遍历订单然后逐个删除。在MQL4里,平掉一个持仓订单可以用OrderClose()函数,它需要订单号、手数和价格这三个参数。但遍历订单有个坑,就是当你平掉一个订单后,订单列表会发生变化,如果你还用原来的循环索引继续往下走,可能会漏掉订单或者报错。所以正确的处理方式是倒序循环,也就是从最后一个订单开始往前遍历,这样即使前面的订单被平掉了,也不影响后面的索引位置。
代码结构大致是这样的:先判断是否满足时间条件,满足的话就进入一个循环,用OrdersTotal()获取当前订单数量,然后从OrdersTotal()-1开始倒着循环到0。每次循环都用OrderSelect(i, SELECT_BY_POS, MODE_TRADES)选中对应位置的订单,然后判断订单类型是买单还是卖单,是买单的话就用MarketInfo(OrderSymbol(), MODE_BID)获取当前买价来平仓,是卖单就用MODE_ASK价格。这里价格一定要用对,不然会报错。
另外还应该加上一个防止重复平仓的机制。因为EA在满足时间条件后,可能会在每次价格跳动时都尝试执行平仓逻辑,如果第一次已经把所有订单都平掉了,第二次循环时OrdersTotal()就变成0了,自然不会有任何操作,这个倒是不会出问题。但如果你在平仓后还开了新仓,那就麻烦了,所以最好加一个布尔变量作为标记,比如bool closeDone = false,平仓执行过一次后就把它设为true,下次再触发时直接跳过。
关于滑点的设置,OrderClose()函数里有一个滑点参数,建议设置成比较大的值,比如30或者50个点。因为周五尾盘流动性变差,点差会拉大,如果滑点设置太小,平仓指令可能会被拒绝。我自己在实际使用中遇到过几次滑点不足导致平仓失败的情况,后来干脆直接写100,反正周五尾盘能平掉就比什么都强。
测试EA平仓功能时容易忽略的细节与常见坑
写完EA后一定要先在MT4自带的策略测试器里跑一遍历史数据,但这里有个问题,策略测试器里的历史时间可能不会精确模拟周五收盘前后的服务器时间变化,所以最好的测试方法是直接挂到模拟账户上,然后手动修改电脑的系统时间?实际上这个是行不通的,因为MT4的服务器时间是由服务器端控制的,你改本地时间没用。
比较靠谱的测试方式是,把EA的时间条件临时改成一个很快就能触发的时间点,比如设置成当前服务器时间的前一分钟,然后等一分钟看看EA能不能触发平仓。这种方法虽然有点笨,但确实能快速验证逻辑是否正确。我之前调试的时候就是这么干的,把条件改成Hour()==当前小时 && Minute()==当前分钟-1,然后挂上EAMT4指标参数加载时动态调整属性面板实时修改全解观察,几分钟就知道代码有没有问题了。
还有一个容易被忽略的点,就是EA必须勾选“允许实时自动交易”选项,不然EA根本不会执行任何下单或平仓操作。在MT4的工具栏上有一个笑脸图标,点一下它会变成哭脸,哭脸就表示自动交易被禁用了。很多新手写完EA挂在图表上,发现半天没反应,结果一看是自动交易没开启,这个低级错误其实挺常见的。
另外提醒一下,如果你同时开着多个图表,每个图表上都挂了这个EA,那平仓逻辑会被重复执行多次。虽然因为订单已经被平掉了不会出大问题,但可能会导致日志里出现大量重复的提示信息,看起来比较混乱。建议只在一个图表上挂EA,或者加一个全局变量判断是否已经执行过平仓操作。使用GlobalVariableSet()和GlobalVariableGet()可以在不同图表之间共享标记,这样即使多个图表都挂着同一个EA,也只会执行一次平仓。