分离策略在趋势和盘整条件下的优化·进阶篇
⚖️

分离策略在趋势和盘整条件下的优化·进阶篇

(2/3)·多数EA在单边与震荡里共用一套参数,回测漂亮实盘漏风,问题出在没分开调

含代码示例偏理论 第 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 用同一阈值容易误触发。

MQL5 / C++
  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 的联合判断。

MQL5 / C++
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 前置校验,能少踩很多初始化期的假信号。外汇与贵金属波动剧烈,这类防御性写法只降低程序错误概率,不预示任何盈利。

MQL5 / C++
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 周期都写死了——组合版取消了训练开关,参数直接吃单策略的旧最优值。

MQL5 / C++
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">//--- 平均速度指标的参数
让小布替你跑这套双相回测
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到趋势段与盘整段的参数适配提示,把重复劳动交给小布,你专注决策。

常见问题

普通优化给整套策略找一组全局最优参数;分离优化分别给上升、下降趋势或盘整区间各找一组,承认市场双相特征不同,可能降低震荡期误信号。
它输出彩色OSMA柱图,柱图增长且值小于0配合平均速度指标过阈值倾向触发买入;柱图下跌且值偏大配合阈值倾向触发卖出,具体阈值需回测确定。
小布目前提供品种页的趋势/盘整分段与参数适配参考,完整EA分离优化仍建议在MT5策略测试器按本文方法手动跑,外汇贵金属属高风险,结果仅作概率参考。
可在通道突破幅度上加过滤器,或接入趋势模块暂停盘整信号;分离优化思路下,盘整参数组不应在趋势样本里强行拟合。
市场微观结构缓慢漂移,原参数对应统计特征弱化,定期重测分离参数组是维持概率优势的常见做法,不保证持续盈利。