分离策略在趋势和盘整条件下的优化(基础篇)
◍ 分离优化在趋势与盘整下的基本思路
把一套策略按市场状态拆开优化,是提升 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 默认值就能切换测试视角,外汇与贵金属波动剧烈,参数直接上实盘前务必先在策略测试器跑分离回测。
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 文件头部,编译后直接在属性框里拖参数,比改源码安全。
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 拦掉同魔术码重复开仓。外汇与贵金属杠杆高,历史信号不代表未来,参数乱调可能频繁止损。
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() {