使用Python和MQL5进行交易策略的自动参数优化·综合运用
(3/3)· 接前两篇铺垫,本篇用均线交叉EA跑通暴力/随机搜索并对照开优化前后的净值曲线
◍ 双均线参数遍历与信号抓取的代码骨架
换一种优化思路,核心就是改这段双层循环里的周期起止与步长,把 fast/slow 的 MA 周期组合全跑一遍,再用回测函数挑出当前准则下更优的那组参数。 指标句柄不释放,图形窗口迟早被堆满,这是 MT5 跑批量优化时最容易踩的坑,EA 里记得在退出前用 IndicatorRelease 清掉。 下面这段是实盘信号部分的骨架:先按当前最优周期建 iMA 句柄,用 CopyBuffer 取最近两根 K 的快慢线数值,再做穿越判断。注意 CopyBuffer 第三个参数填 0、长度填 2,意味着拿到的是 [0] 当前与 [1] 前一根的序列值(因 ArraySetAsSeries 设为 true)。 买入判定写得很直白——前一根快线在慢线下方、当前快线翻到上方,即视为金叉;进场前先扫一遍 PositionsTotal 平掉已有空单。外汇与贵金属杠杆高,这类交叉信号在历史中胜率可能仅四成到六成,实盘前务必用策略测试器按品种校准周期步长。
for(class="type">int fastPeriod = FastMAPeriodStart; fastPeriod <= FastMAPeriodStop; fastPeriod += FastMAPeriodStep) { for(class="type">int slowPeriod = SlowMAPeriodStart; slowPeriod <= SlowMAPeriodStop; slowPeriod += SlowMAPeriodStep) { class="type">class="kw">double criterionValue = PerformBacktest(fastPeriod, slowPeriod, startBar); if(IsNewOptimal(criterionValue, bestCriterionValue, OptimizationCriterion)) { bestCriterionValue = criterionValue; bestFastPeriod = fastPeriod; bestSlowPeriod = slowPeriod; } } } class="type">class="kw">double fastMA_curr[]; class="type">class="kw">double slowMA_curr[]; class="type">class="kw">double fastMA_prev[]; class="type">class="kw">double slowMA_prev[]; ArraySetAsSeries(fastMA_curr, true); ArraySetAsSeries(slowMA_curr, true); ArraySetAsSeries(fastMA_prev, true); ArraySetAsSeries(slowMA_prev, true); class="type">int fastMA_current = iMA(_Symbol, class="num">0, FastMAPeriod, class="num">0, MODE_SMA, PRICE_CLOSE); class="type">int fastMA_previous = iMA(_Symbol, class="num">0, FastMAPeriod, class="num">1, MODE_SMA, PRICE_CLOSE); class="type">int slowMA_current = iMA(_Symbol, class="num">0, SlowMAPeriod, class="num">0, MODE_SMA, PRICE_CLOSE); class="type">int slowMA_previous = iMA(_Symbol, class="num">0, SlowMAPeriod, class="num">1, MODE_SMA, PRICE_CLOSE); CopyBuffer(fastMA_current, class="num">0, class="num">0, class="num">2, fastMA_curr); CopyBuffer(slowMA_current, class="num">0, class="num">0, class="num">2, slowMA_curr); CopyBuffer(fastMA_previous, class="num">0, class="num">0, class="num">2, fastMA_prev); CopyBuffer(slowMA_previous, class="num">0, class="num">0, class="num">2, slowMA_prev); class="type">class="kw">double fastMA_previousFF = fastMA_prev[class="num">0]; class="type">class="kw">double slowMA_previousSS = slowMA_prev[class="num">0]; class="type">class="kw">double fastMA_currentFF = fastMA_curr[class="num">0]; class="type">class="kw">double slowMA_currentSS = slowMA_curr[class="num">0]; class=class="str">"cmt">// Check for buy signal(fast MA crosses above slow MA) if(fastMA_previousFF < slowMA_previousSS && fastMA_currentFF > slowMA_currentSS) { class=class="str">"cmt">// Close any existing sell positions if(PositionsTotal() > class="num">0) { for(class="type">int i = PositionsTotal() - class="num">1; i >= class="num">0; i--) { if(PositionSelectByTicket(i)) { if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
「双均线死叉时的平仓与反手逻辑」
当快线前一根高于慢线、当前根跌破慢线,系统判定为做空信号。此时先扫一遍持仓池,把所有 BUY 单按 ticket 逐个平掉,再以 SYMBOL_BID 市价开 SELL。 平仓循环用 PositionsTotal()-1 倒序遍历,PositionSelectByTicket(i) 选中后检查 POSITION_TYPE 是否为 POSITION_TYPE_BUY,命中才打印并 ClosePosition。倒序是为了避免删除持仓导致索引错位。 开仓价取卖价而非买价,ATR 倍数沿用外部变量 ATRmultiplier,传给 OpenPosition 做止损距离计算。外汇与贵金属杠杆高,死叉反手可能在震荡市连续挨巴掌,参数需先在 MT5 策略测试器跑历史段验证。 信号处理完,四根指标句柄 fastMA_current / slowMA_current / fastMA_previous / slowMA_previous 都用 IndicatorRelease 释放,防止 EA 长期运行句柄泄漏拖慢终端。
Print("Closing sell position: Ticket ", PositionGetInteger(POSITION_TICKET)); ClosePosition(PositionGetInteger(POSITION_TICKET)); } } } } class=class="str">"cmt">// Open a buy position class="type">class="kw">double openPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); class="type">class="kw">double atrMultiplier = ATRmultiplier; OpenPosition(ORDER_TYPE_BUY, openPrice, atrMultiplier); } class=class="str">"cmt">// Check for sell signal(fast MA crosses below slow MA) else if(fastMA_previousFF > slowMA_previousSS && fastMA_currentFF < slowMA_currentSS) { class=class="str">"cmt">// Close any existing buy positions if(PositionsTotal() > class="num">0) { for(class="type">int i = PositionsTotal() - class="num">1; i >= class="num">0; i--) { if(PositionSelectByTicket(i)) { if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { Print("Closing buy position: Ticket ", PositionGetInteger(POSITION_TICKET)); ClosePosition(PositionGetInteger(POSITION_TICKET)); } } } } class=class="str">"cmt">// Open a sell position class="type">class="kw">double openPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); class="type">class="kw">double atrMultiplier = ATRmultiplier; OpenPosition(ORDER_TYPE_SELL, openPrice, atrMultiplier); } IndicatorRelease(fastMA_current); IndicatorRelease(slowMA_current); IndicatorRelease(fastMA_previous); IndicatorRelease(slowMA_previous); }
EURUSD 一个月实跑:自优化开与关的差距
拿 EURUSD 做对照,取 2024 年 4 月 20 日至 5 月 20 日这一段,时间间隔固定为 1 天,分别跑启用与未启用自优化的同一套逻辑。 未开自优化时,这 30 天里账户没有任何成交——策略在样本内完全沉默,等于空转。 开了自优化的那一份,起始先做一次自优化拟合,随后在 40 天周期节点上触发重新优化(该重优化点落在所研究时段之外)。从结果看,自优化版本明显更可能持续产出信号,关掉则大概率全程零交易。外汇与贵金属属高杠杆品种,这种对照仅说明机制差异,不预示任何收益。
◍ 把工具请下神坛
EA 自优化的核心价值不在「自动赚钱」,而在把参数维护从手工苦力变成可复现流程。前面几节给出的 MA 交叉案例中,用 Python 做 grid / random search 回测,再借 MQL5 把重优化周期写进 EA——评论区有人照搬参数却没交易,作者点出是重优化天数设成了 22 天而样例该用 16 天,这种细节只能自己跑一遍 MT5 才锁得住。 外汇与贵金属自带高杠杆与跳空风险,自优化降低的是「参数过期」的概率,不是消除账户回撤的可能。把这套机制当校验器而非印钞机,才接近原文里说的「信心与安心」。 下次开 MT5 载入 Brute-Force_0031.mq5,先把重新优化间隔改成 16 天跑一轮 2023 年 EURUSD 数据,看 equity 曲线和裸跑版本的差值,比读十篇综述都实在。