分离策略在趋势和盘整条件下的优化(基础篇)
📘

分离策略在趋势和盘整条件下的优化(基础篇)

第 1/3 篇

◍ 分离优化在趋势与盘整下的基本思路

把一套策略按市场状态拆开优化,是提升 MT5 回测稳健性的实用路子。核心假设很简单:趋势行情和盘整行情里,同一组参数往往表现迥异,硬套全局最优容易过拟合。 原文作者在 2019 年 2 月发布的测试框架中,分别挑了跟随趋势类与基于盘整类各一套策略做对照,样本覆盖 EURUSD 等多品种,历史 tick 数据回测周期超过 3 年。 落地动作很直接:在 MT5 策略测试器里,先用「行情分段」把数据切成趋势段和震荡段,各自跑遗传优化,再把两段的最优参数集作为组合策略的切换依据。这样你开 MT5 就能复现,不需要任何外部库。

策略为何总得跟着市场调参数

写EA第一件事就是定入场、持仓跟踪和出场规则,背后靠的是数学统计外加指标这类自治系统去刻画市场特征。但有个绕不开的硬伤:没有哪种系统能在所有行情里同样好用,趋势型在漫长横盘里会连续吐利润,区间型碰上单边突破又容易误判进场。 为压住错误信号,大家把系统做成可调参的灵活结构,留出一串 input 让使用者按市况改。MetaTrader 5 自带的策略测试器就是干这个的——它能在历史数据上跑你的EA,帮你挑出更适配当下环境的参数组合。 实际经验里,任何系统效率都会随时间衰减,半年前最优的止损倍数放到现在可能就成了被扫的命,所以定期回测重标定输入值是常态动作,外汇和贵金属杠杆高、跳空频繁,调参前先用测试器跑通再上实盘。

「把上涨和下跌拆开调参」

多数双向策略用同一组参数去框住上升和下降趋势,但实战里两段行情的特征差异很大。以图 1 的趋势跟踪为例,低买高卖的逻辑简单,可上涨的速度、持续时间往往和下跌不是一回事,硬套同一套进场条件容易在某一侧吃亏。 这里引出一个做法:把上升趋势和下降趋势拆成两套参数空间,各自定义进场与退场条件。我们叫它“分离优化”——用 MT5 策略测试器分别为涨、跌两段寻最优参数,而不是混在一起寻一个折中值。 为验证这个概念,我挑了两个系统做样本:趋势跟随系统做上升/下降分离优化,盘整系统则评估通道内交易的优化效果。外汇与贵金属波动剧烈,分离优化只降低过拟合某一侧的概率,不保证实盘胜率,请先在历史数据回测确认。

◍ 用重心OSMA搭一套分趋势EA

趋势跟随策略里,我选了 Ehlers 的重心指标 CenterOfGravityOSMA 作信号源,辅以平均速度指标确认。前者在 H1 上以彩色 OSMA 柱呈现:柱体增长且指标值小于 0 倾向多头,柱体下跌且指标值大于 0 倾向空头,平均速度需高于预设阈值才放行信号。 实盘层面,这套逻辑被拆成可独立运行的三种模式:仅上升、仅下降、两者并行。分离优化的意义在于,上升与下降行情应各有独立参数集,避免在联合交易时互相污染。 MT5 里落地的关键,是 Inp_Trend_type 这个枚举变量。它决定 EA 跑哪种市况;选 BOTH 时,Uptrend 和 Downtrend 两套参数同时加载,系统各自按自己的止损止盈独立下单。 下方代码里,止损 40 点、获利 60 点仅属于上升趋势块(MagicNum 1111)。你开 MetaEditor 把这段贴进去,改 Inp_Trend_type 默认值就能切换测试视角,外汇与贵金属波动剧烈,参数直接上实盘前务必先在策略测试器跑分离回测。

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">//+------------------------------------------------------------------+
input class="type">class="kw">string            Inp_EaComment="Trend 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">//资金管理
input Trend_type        Inp_Trend_type=class="num">3;                        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">40;                       class=class="str">"cmt">//止损(点数)
input class="type">int               Inp_TakeProfit1=class="num">60;                     class=class="str">"cmt">//获利(点数)
class=class="str">"cmt">//--- CenterOfGravityOSMA 指标参数

双指标 EA 的输入参数怎么配

下面这段 MQL5 输入块来自一个同时跑重心 OSMA 与平均速度指标的 EA,把多组外部参数一次性暴露给 MT5 终端,方便交易者不碰代码就能调。 先看趋势与均线部分:平均周期 Period_1 默认 9,两段平滑 SmoothPeriod1_1 / SmoothPeriod2_1 都设 3,方法用 MODE_SMA,应用价取 PRICE_OPEN_;平均速度子模块另配 Inp_Bars1=1(按日)、Price1=PRICE_CLOSE、Trend_lev1=2 作为趋势判定门槛。 下降趋势那组给了实打实的出场距离:Inp_StopLoss2=40 点、Inp_TakeProfit2=60 点,幻数 2222 用来隔离本 EA 的订单。外汇与贵金属杠杆高,40/60 点止损止盈在 EURUSD 小周期可能刚够噪点,换 XAUUSD 就容易被扫,参数必须按品种重校。 重心 OSMA 这边 Period_2 同样 9、SmoothPeriod1_2 为 3,和前面均线保持同周期方便共振观察。复制下面代码到 MQ5 文件头部,编译后直接在属性框里拖参数,比改源码安全。

MQL5 / C++
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_OPEN_;                                                         class=class="str">"cmt">//使用的价格
class=class="str">"cmt">//--- 平均速度指标的参数
input class="type">int                Inp_Bars1=class="num">1;                                                                       class=class="str">"cmt">//天数
input ENUM_APPLIED_PRICE Price1=PRICE_CLOSE;                                                                class=class="str">"cmt">//使用的价格
input class="type">class="kw">double             Trend_lev1=class="num">2;                                                                      class=class="str">"cmt">//趋势水平
class=class="str">"cmt">//--- 下降趋势参数
input class="type">class="kw">string             Inp_Str_label2="===Downtrend parameters==="; class=class="str">"cmt">//标签
input class="type">int                Inp_MagicNum2=class="num">2222;                                                                class=class="str">"cmt">//幻数
input class="type">int                Inp_StopLoss2=class="num">40;                                                                  class=class="str">"cmt">//止损(点数)
input class="type">int                Inp_TakeProfit2=class="num">60;                                                                class=class="str">"cmt">//获利(点数)
class=class="str">"cmt">//--- CenterOfGravityOSMA 指标参数
input class="type">uint               Period_2=class="num">9;                                                                        class=class="str">"cmt">//平均周期数
input class="type">uint               SmoothPeriod1_2=class="num">3;                                                                 class=class="str">"cmt">//平滑周期数1

「双模式开关怎么接进 OnTick」

这套 EA 把趋势模式做成 input 三选一:1 只做多、2 只做空、3 双向都跑。OnTick 里先调 GetIndValue() 取指标值,返回 false 就直接 return,避免空值下单。 下面的输入参数暴露了第二套均线的细节:平滑周期 SmoothPeriod2_2=3、平均方法 MA_Method_2_2=MODE_SMA、取价 AppliedPrice2=PRICE_OPEN_;另外平均速度指标用 Inp_Bars2=1 根 K 线、Price2=PRICE_CLOSE、Trend_lev2=2 作为趋势阈值。把这些接进 MT5 输入变量,就能在实盘前改参数验证。 Inp_Trend_type==3 时,BuySignal 与 SellSignal 各自独立判断,且都用 !Trade.IsOpenedByMagic 拦掉同魔术码重复开仓。外汇与贵金属杠杆高,历史信号不代表未来,参数乱调可能频繁止损。

MQL5 / C++
input ENUM_MA_METHOD     MA_Method_1_2=MODE_SMA;            class=class="str">"cmt">//平均方法1
input class="type">uint                SmoothPeriod2_2=class="num">3;                  class=class="str">"cmt">//平滑周期数2
input ENUM_MA_METHOD     MA_Method_2_2=MODE_SMA;             class=class="str">"cmt">//平均方法2
input Applied_price_      AppliedPrice2=PRICE_OPEN_;         class=class="str">"cmt">//使用的价格
class=class="str">"cmt">//--- 平均速度指标的参数
input class="type">int                 Inp_Bars2=class="num">1;                       class=class="str">"cmt">//天数
input ENUM_APPLIED_PRICE  Price2=PRICE_CLOSE;                class=class="str">"cmt">//使用的价格
input class="type">class="kw">double              Trend_lev2=class="num">2;                      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()
  {

常见问题

大概率是因为策略用了同一套参数应对不同市况;把趋势和盘整分开诊断、分别给参数,回撤可能更小。
可按多空方向各跑一组样本外测试,分别记录最优止损与过滤阈值;用脚本批量跑比肉眼翻K线靠谱。
小布盯盘的AIGC会按品种页的波动与重心指标给出市况倾向,你照着切双模式开关就行。
至少留OSMA周期、重心平滑、多空阈值与模式开关;参数越少越不容易过拟合。
多在每次 tick 重复初始化或没做状态去重;把模式判断放最前、只切一次状态即可避免反复加载。