MQL5自优化智能交易系统(第九部分):双移动平均线交叉·进阶篇
◍ 先把均线柄和波动垫读进内存
这段初始化逻辑干的事很直接:把两条自定义均线的当前值用 CopyBuffer 拉进数组 ma_c_4、ma_c_3,各取 1 根(起始 0、数量 1),同时用 SymbolInfoDouble 抓当前品种的 bid 与 ask。 接着在 TF_3 周期上取最近 10 根 K 线的高低数组,high 和 low 都用 vector::Zeros(10) 预分配,再靠 CopyRates 填数。两者相减得到每根波幅向量 var,var.Mean() 就是这 10 根的平均真实波幅,我们叫它 padding——后续挂单间距和止损垫都参考它。 仓位判断放在末尾:PositionsTotal() 为 0 就跑 find_setup(padding) 找信号;大于 0 则进 manage_setup() 管仓。外汇与贵金属杠杆高,padding 只是波动度量,不代表任何方向确定性,开仓前请在 MT5 策略测试器用真实点差回测。
CopyBuffer(ma_c_4_handle,class="num">0,class="num">0,class="num">1,ma_c_4); CopyBuffer(ma_c_3_handle,class="num">0,class="num">0,class="num">1,ma_c_3); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); vector high = vector::Zeros(class="num">10); vector low = vector::Zeros(class="num">10); low.CopyRates(Symbol(),TF_3,COPY_RATES_LOW,class="num">0,class="num">10); high.CopyRates(Symbol(),TF_3,COPY_RATES_HIGH,class="num">0,class="num">10); vector var = high - low; class="type">class="kw">double padding = var.Mean(); class=class="str">"cmt">//Find an open position if(PositionsTotal() == class="num">0) find_setup(padding); class=class="str">"cmt">//Manage our open positions else if(PositionsTotal() > class="num">0) manage_setup(); }
把止损周期交给优化器反而卡在样本外
上一版里止损缓冲写死用 10 根 H4 K线,这次改成让遗传优化器自己挑时间框架和历史根数,顺手把 update 里的止损逻辑拆出去。原 update 既算缓冲又管仓位,现在拆成 get_padding 算缓冲、find_setup 找机会、manage_setup 管持仓,止损能跟着盈利仓位往前挪。 代码里输入组暴露了 TF_3 和 HISTORICAL_BARS 两个变量,get_padding 默认拉 10 根 TF_3 的高低点算均值当 padding,update 只负责刷指标和调这两个子函数。想验证就把这段贴进 EA,把 HISTORICAL_BARS 从 10 改成允许 5~50 区间让优化器跑。 我拿一套回测和前瞻都盈利的参数做底,只放开风险参数让优化器找更优解。结果图 9、图 10 都显示:新组合在前瞻盈利、回测亏,老问题原样复现——优化器挑的风险设置过拟合了样本内。外汇和贵金属杠杆高,这种样本外崩坏的现象意味着实盘大概率踩坑,别直接信优化报告。
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">//| Inputs |</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="keyword">input</span> group <span class="class="type">class="kw">string">"Money Management Settings"</span> <span class="keyword">input</span> <span class="macro">ENUM_TIMEFRAMES</span> TF_3 = <span class="macro">PERIOD_H4</span>; <span class="comment">class=class="str">"cmt">//Risk Time Frame</span> <span class="keyword">input</span> <span class="keyword">class="type">int</span> HISTORICAL_BARS = <span class="number">class="num">10</span>; <span class="comment">class=class="str">"cmt">//Historical bars for risk calculation</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">//| Get the stop loss size to use |</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="keyword">class="type">class="kw">double</span> get_padding(<span class="keyword">class="type">void</span>) { <span class="keyword">vector</span> high = <span class="keyword">vector</span>::Zeros(<span class="number">class="num">10</span>); <span class="keyword">vector</span> low = <span class="keyword">vector</span>::Zeros(<span class="number">class="num">10</span>); low.<span class="functions">CopyRates</span>(<span class="functions">Symbol</span>(),TF_3,<span class="macro">COPY_RATES_LOW</span>,<span class="number">class="num">0</span>,HISTORICAL_BARS); high.<span class="functions">CopyRates</span>(<span class="functions">Symbol</span>(),TF_3,<span class="macro">COPY_RATES_HIGH</span>,<span class="number">class="num">0</span>,HISTORICAL_BARS); <span class="keyword">vector</span> var = high - low; <span class="keyword">class="type">class="kw">double</span> padding = var.Mean(); <span class="keyword">class="kw">return</span>(padding); } <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">//| Update our technical indicators and positions |</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="keyword">class="type">void</span> update(<span class="keyword">class="type">void</span>) { <span class="comment">class=class="str">"cmt">//Update technical indicators and market readings</span> <span class="functions">CopyBuffer</span>(ma_c_2_handle,<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,ma_c_2); <span class="functions">CopyBuffer</span>(ma_c_1_handle,<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,ma_c_1); <span class="functions">CopyBuffer</span>(ma_c_4_handle,<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,ma_c_4); <span class="functions">CopyBuffer</span>(ma_c_3_handle,<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,ma_c_3); bid = <span class="functions">SymbolInfoDouble</span>(<span class="functions">Symbol</span>(),<span class="macro">SYMBOL_BID</span>); ask = <span class="functions">SymbolInfoDouble</span>(<span class="functions">Symbol</span>(),<span class="macro">SYMBOL_ASK</span>); <span class="keyword">class="type">class="kw">double</span> padding = get_padding(); <span class="comment">class=class="str">"cmt">//Find an open position</span> <span class="keyword">if</span>(<span class="functions">PositionsTotal</span>() == <span class="number">class="num">0</span>) find_setup(padding); <span class="comment">class=class="str">"cmt">//Manage our open positions</span> <span class="keyword">else</span> <span class="keyword">if</span>(<span class="functions">PositionsTotal</span>() > <span class="number">class="num">0</span>) manage_setup(); } <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">//| Manage our open positions |</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
「持仓后的止损平移与均线出场」
这段函数管的是开仓之后怎么盯盘:先确认当前品种有没有持仓,再按多空状态把止损往有利方向挪,最后用两组均线交叉决定要不要平掉。 卖单(state==0)时,新止损算成 ask 加 padding;只有当新止损比原止损更靠下,才调用 PositionModify 把 SL 改掉。买单(state==1)反过来,新止损是 bid 减 padding,且只在比原 SL 更高时才改,避免把止损往亏损方向拉。 出场分两个时间框架开关。closing_tf==0 用 ma_c_1 与 ma_c_2 的零号元素比较:卖单遇到快线高于慢线、买单遇到快线低于慢线就直接 PositionClose。closing_tf==1 则切到 ma_c_3 与 ma_c_4 做同样判断。 把 padding 调小,止损跟得更紧但可能被扫得频繁;调大则容错高、回撤也大。外汇和贵金属波动剧烈,这套逻辑在实盘里可能频繁触发修改与平仓,建议先上 MT5 策略测试器用历史数据跑一遍再上真仓。
class="type">void manage_setup(class="type">void) { class=class="str">"cmt">//Does the position exist? if(PositionSelect(Symbol())) { class=class="str">"cmt">//Get the current stop loss class="type">class="kw">double current_sl = PositionGetDouble(POSITION_SL); class="type">class="kw">double padding = get_padding(); class="type">class="kw">double new_sl; class=class="str">"cmt">//Sell position if((state == class="num">0)) { new_sl = (ask + padding); if(new_sl < current_sl) Trade.PositionModify(Symbol(),new_sl,class="num">0); } class=class="str">"cmt">//Buy position if((state == class="num">1)) { new_sl = (bid - padding); if(new_sl > current_sl) Trade.PositionModify(Symbol(),new_sl,class="num">0); } if(closing_tf == class="num">0) { if((state ==class="num">0) && (ma_c_1[class="num">0] > ma_c_2[class="num">0])) Trade.PositionClose(Symbol()); if((state ==class="num">1) && (ma_c_1[class="num">0] < ma_c_2[class="num">0])) Trade.PositionClose(Symbol()); } else if(closing_tf == class="num">1) { if((state ==class="num">0) && (ma_c_3[class="num">0] > ma_c_4[class="num">0])) Trade.PositionClose(Symbol()); if((state ==class="num">1) && (ma_c_3[class="num">0] < ma_c_4[class="num">0])) Trade.PositionClose(Symbol()); } } }
◍ 全参数搜索才是下一步
双均线交叉的这次改造证明了一件事:滞后性可以被压,但单点调参的红利有限。只动一个参数、焊死其他变量,往往看不到全局最优解在哪。 把均线周期、止损幅度、过滤阈值全部放开做遗传优化,回测里更容易撞出在样本内和样本外都站得住的组合。外汇与贵金属波动跳变频繁,这类全参数搜索只是降低滞后概率,不承诺任何收益。 等最优参数落定,再拿它们训一个统计模型去预判交叉质量,比裸跑均线更稳。但眼下只是开头——优化器没批量吐出前瞻测试仍盈利的 EA 之前,说明跑得还不够多。 开发者自己不下场反复跑流程,AI 替代不了这份苦功。市场高风险,参数漂亮和实盘能活是两回事。