分离策略在趋势和盘整条件下的优化·进阶篇
(2/3)·多数EA在单边与震荡里共用一套参数,回测漂亮实盘漏风,问题出在没分开调
多指标联合触发买卖信号的逻辑
这套信号函数把重心放在 COG(重心线)斜率与平均速率的联合判定上。买条件要求 avr_speed1[0] 大于 Trend_lev1,且 cog1 最新值高于前一根、两根都为负,意味着下行重心在降速后拐头。 卖条件对称:avr_speed2[0] 大于 Trend_lev2,cog2 最新值低于前一根、两根都为正,对应上行重心涨速放缓后反转。外汇与贵金属杠杆高,这类信号只代表概率倾斜,不代表方向锁死。 GetIndValue 负责把四个句柄的缓冲拉进数组,每次取 0~1 两根。只要任意一个 CopyBuffer 返回值 ≤0,就判为取数失败、整体返回 false,避免用脏数据发单。 实盘前把 Trend_lev1 / Trend_lev2 按品种波动率重设,EURUSD 与 XAUUSD 用同一阈值容易误触发。
class="kw">return(avr_speed1[class="num">0]>Trend_lev1 && cog1[class="num">1]<cog1[class="num">0] &&(cog1[class="num">1]<class="num">0 && cog1[class="num">0]<class="num">0))?true:false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 卖出条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool SellSignal() { class="kw">return(avr_speed2[class="num">0]>Trend_lev2 && cog2[class="num">1]>cog2[class="num">0] &&(cog2[class="num">1]>class="num">0 && cog2[class="num">0]>class="num">0))?true:false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 取得当前指标值 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool GetIndValue() { class="kw">return(CopyBuffer(InpInd_Handle1,class="num">0,class="num">0,class="num">2,cog1)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">0,class="num">0,class="num">2,cog2)<=class="num">0 || CopyBuffer(InpInd_Handle3,class="num">0,class="num">0,class="num">2,avr_speed1)<=class="num">0 || CopyBuffer(InpInd_Handle4,class="num">0,class="num">0,class="num">2,avr_speed2)<=class="num">0 )?false:true; } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 用单向行情先榨参数再丢混合段验成色
想把策略里的指标参数、止损止盈值调到位,最省事的做法是先把行情按方向拆开:上涨段只喂给上升模式,下跌段只喂给下降模式,各自优化互不干扰。我实际选了两组区间——上升模式用 2017.04.10 至 2018.02.01,下降模式用 2014.05.08 至 2015.03.13,品种锁 EURUSD,M1 OHLC 跑测试,初始存款 1000 USD、杠杆 1:500、MetaQuotes-Demo 五位数报价,无延迟模式(非高频,延迟扰动可忽略)。 参数在单向段里榨出来后,必须扔进一段既含上升也含下跌、还夹着回滚和盘整的混合区间,否则只是过拟合单向市。我挑的混合段覆盖多种状态,拿前面优化好的参数直接跑陌生数据。 检验结果偏正面:策略在不利区间净值为正,多头成交成功率明显高于空头;盈利能力和预期回报都指向正向动力。外汇与贵金属属高风险品种,这段正向仅代表该样本区间概率倾向,换品种或换年段可能失效,开 MT5 用同设定复跑是最直接的验证动作。
「用 WPR 加 ADX 卡盘整区间的入场逻辑」
盘整市里单独跑一套策略,核心是把「没有趋势」先确认下来。这里选威廉姆斯百分比范围(WPR)抓超买超卖,再用 ADX 做无趋势过滤器:只有 ADX 低于阈值,才承认市场处于横盘。 具体规则放在 H1 周期上——买入看 WPR 跌穿 -80 且 ADX 在阈值下;卖出看 WPR 冲过 -20 且 ADX 同样在阈值下。两个阈值(Inp_FlatLevel1 / Inp_FlatLevel2)分开设,方便多空各自调灵敏度。 EA 把方向拆成三种模式:只多、只空、多空都跑。BOTH 模式下同一根 bar 可能双边挂单,实盘前要在 MT5 里确认点差和库存费不会吃掉窄幅利润。外汇与贵金属杠杆高,横盘假突破频繁,这套逻辑也只是「倾向」提高胜率,不保证出场为正。 下面这段是策略的骨架代码,信号函数就两行,重点在 wpr 与 adx 的联合判断。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 操作模式枚举 | class=class="str">"cmt">//+------------------------------------------------------------------+ enum Trend_type { UPTREND = class="num">1, class=class="str">"cmt">//买入 DOWNTREND, class=class="str">"cmt">//卖出 BOTH class=class="str">"cmt">//同时 }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA交易分时函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- 取得计算数据 if(!GetIndValue()) class="kw">return; if(Inp_Trend_type==class="num">1 && !Trade.IsOpenedByMagic(Inp_MagicNum1)) { class=class="str">"cmt">//--- 如果有买入信号,开启订单 if(BuySignal()) Trade.BuyPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss1,Inp_TakeProfit1,Inp_MagicNum1,Inp_EaComment); } else if(Inp_Trend_type==class="num">2 && !Trade.IsOpenedByMagic(Inp_MagicNum2)) { class=class="str">"cmt">//--- 如果有卖出信号,开启订单 if(SellSignal()) Trade.SellPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss2,Inp_TakeProfit2,Inp_MagicNum2,Inp_EaComment); } else if(Inp_Trend_type==class="num">3) { class=class="str">"cmt">//--- 如果有买入信号,开启订单 if(BuySignal() && !Trade.IsOpenedByMagic(Inp_MagicNum1)) Trade.BuyPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss1,Inp_TakeProfit1,Inp_MagicNum1,Inp_EaComment); class=class="str">"cmt">//--- 如果有卖出信号,开启订单 if(SellSignal() && !Trade.IsOpenedByMagic(Inp_MagicNum2)) Trade.SellPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss2,Inp_TakeProfit2,Inp_MagicNum2,Inp_EaComment); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 买入条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool BuySignal() { class="kw">return(wpr1[class="num">0]<-class="num">80 && adx1[class="num">0]<Inp_FlatLevel1)?true:false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 卖出条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool SellSignal() { class="kw">return(wpr2[class="num">0]>=-class="num">20 && adx2[class="num">0]<Inp_FlatLevel2)?true:false; }
用 CopyBuffer 守住指标句柄的有效性
多指标共振策略里,最容易被忽略的不是信号逻辑,而是指标句柄是否真的取到了数。上面这段函数把四个句柄的 CopyBuffer 调用一次性做了有效性拦截:只要任意一个返回小于等于 0,整个 GetIndValue 就返回 false,后续信号计算直接跳过。 具体看参数,每次 CopyBuffer 都从偏移 0 开始、拉取 2 根柱线的数据写入对应数组(wpr1/wpr2/adx1/adx2)。在 MT5 里,CopyBuffer 返回的是实际复制到的元素个数,返回 0 或负数代表句柄未就绪或图表数据不足。 实盘里常见坑是:切换周期或刚加载 EA 时前几秒句柄为空,若不做这层判断就会用脏数据下单。把这段直接塞进你的 OnTick 前置校验,能少踩很多初始化期的假信号。外汇与贵金属波动剧烈,这类防御性写法只降低程序错误概率,不预示任何盈利。
class="type">bool GetIndValue() { class="kw">return(CopyBuffer(InpInd_Handle1,class="num">0,class="num">0,class="num">2,wpr1)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">0,class="num">0,class="num">2,wpr2)<=class="num">0 || CopyBuffer(InpInd_Handle3,class="num">0,class="num">0,class="num">2,adx1)<=class="num">0 || CopyBuffer(InpInd_Handle4,class="num">0,class="num">0,class="num">2,adx2)<=class="num">0 )?false:true; }
◍ 盘整策略的回测与逆风验证
盘整策略的检验思路和趋势跟踪类似:先切一段历史做训练与参数优化。本例锁定 2015.03.13—2017.01.01 的 EURUSD,M1 OHLC 跑回测,初始存款 1000 USD、杠杆 1:500、五位数报价、MetaQuotes-Demo 服务器,延迟设为 No delay——这类非高频逻辑受滑点扰动本就有限。 买、卖两模式各自独立优化止盈止损参数,得到两组最优解。随后故意把它丢进此前趋势跟踪测试用过的非盘整区间(长趋势段)做混合压力测试。 结果在逆风区间仍显示盈利。盘整策略本该在震荡外吃亏,实盘却没崩,说明参数鲁棒性可能比单纯拟合更好,但外汇高风险,样本仅两年,换品种或行情结构切换后表现倾向需重验。
「把两套独立逻辑塞进一个EA跑三年」
前面分别训好的趋势跟踪与盘整策略,各自带着有利区间里跑出来的最优参数,现在不重新优化,直接拼进同一个EA里共同工作。四种模式互不交叉,各自用独立幻数下单,相当于让四个不说话的交易员在同一账户里各做各的。 测试区间拉到三年:2015.01.01—2018.11.30,品种 EURUSD,M1 的 OHLC 撮合,初始存款 1000 USD,杠杆 1:500,MetaQuotes-Demo 五位数报价。这类中低频逻辑对滑点不敏感,测试里没加延迟,真实分时下预跑结果也近似。 在共同的不利行情里,四个独立模块合起来账面为正。外汇与贵金属本身高杠杆、高波动,这种组合只说明「在选定样本内未崩」,换品种或周期可能明显走弱,拿去实盘前务必自己重跑一遍。 代码里能看到输入区把手数、资金管理、上升趋势的止损55点/获利50点,以及 CenterOfGravityOSMA 的 Period_1=9、双重 SMA 平滑各 3 周期都写死了——组合版取消了训练开关,参数直接吃单策略的旧最优值。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA 的输入参数 | class=class="str">"cmt">//+------------------------------------------------------------------+ input class="type">class="kw">string Inp_EaComment="Universe Strategy"; class=class="str">"cmt">//EA 注释 input class="type">class="kw">double Inp_Lot=class="num">0.01; class=class="str">"cmt">//手数 input MarginMode Inp_MMode=LOT; class=class="str">"cmt">//资金管理 class=class="str">"cmt">//--- 上升趋势参数 input class="type">class="kw">string Inp_Str_label1="===Uptrend parameters==="; class=class="str">"cmt">//标签 input class="type">int Inp_MagicNum1=class="num">1111; class=class="str">"cmt">//幻数 input class="type">int Inp_StopLoss1=class="num">50; class=class="str">"cmt">//止损(点数) input class="type">int Inp_TakeProfit1=class="num">55; class=class="str">"cmt">//获利(点数) class=class="str">"cmt">//--- CenterOfGravityOSMA 指标参数 input class="type">uint Period_1=class="num">9; class=class="str">"cmt">//平均周期数 input class="type">uint SmoothPeriod1_1=class="num">3; class=class="str">"cmt">//平滑周期数1 input ENUM_MA_METHOD MA_Method_1_1=MODE_SMA; class=class="str">"cmt">//平均方法1 input class="type">uint SmoothPeriod2_1=class="num">3; class=class="str">"cmt">//平滑周期数2 input ENUM_MA_METHOD MA_Method_2_1=MODE_SMA; class=class="str">"cmt">//平均方法2 input Applied_price_ AppliedPrice1=PRICE_TRENDFOLLOW1_; class=class="str">"cmt">//使用的价格 class=class="str">"cmt">//--- 平均速度指标的参数