横盘交易策略比较分析:先搞清横盘的定义与建仓任务(基础篇)
(1/3)·趋势策略在动荡市里越来越难赚钱,横盘行情的潜在收益却常被忽略,本篇先铺底基本概念
很多交易者把横盘当成“没法做”的垃圾时间,要么空仓等趋势,要么硬用趋势系统来回止损。其实窄幅振荡里价格沿通道边界反复折返,是有规律可循的,只是多数人没先定义清楚什么叫横盘、该赚哪一段。
◍ 横盘策略横向比对的起点
这篇比对把 10 种针对横盘行情的交易策略放在同一框架下跑了一遍。原文发布于 2018 年 8 月 17 日,在 MetaTrader 5 社区累计被查看 26 493 次、收藏 13 次,说明横盘处理是实盘里长期被盯着的硬骨头。 横盘的本质是波动收敛、方向缺失,均线类趋势系统在这段里容易反复挨耳光。后面几节会逐一拆这 10 个系统的进出场逻辑与样本表现,本篇先立骨架:所有策略都基于 MT5 回测,不预设哪条更优,只看它们在窄幅区间里的存活率与回撤分布。 外汇与贵金属在横盘期点差侵蚀极快,任何横盘策略都只是概率占优,不是免死金牌。开 MT5 用 EURUSD 的 H1 拉一段 2017 年震荡段,先自己跑一遍基准均线交叉,感受一下磨损再往下看。
十套横盘策略的骨架清单
做横盘交易,先要把「识别区间」和「过滤假突破」拆成两件独立的事。下面这 10 套策略全部沿用同一思路:通道类指标划定震荡边界,叠加一个独立滤波器判定当前是否真处于横盘。 具体组合依次是:MFI 滤波的轨道线、布林带加双均线、Ehlers 分形维度滤波的 WSO/WRO 通道、通道交叉百分比配趋势范围滤波、价格通道配 RBVI 滤波、威廉姆斯 %R 配 ADX 滤波、改良 Keltner 配魔幻趋势滤波、唐奇安通道配三位一体脉动确认、ATR 通道配 CCI 色阶滤波、RSI 直方图配横盘指标滤波。 外汇与贵金属在横盘期点差容易扩大,这类策略在 EURUSD 日线回测中约有 60% 样本能跑出正期望,但贵金属 XAUUSD 在重大数据周常出现通道外 200 点以上的瞬刺,实盘前务必在 MT5 用历史中心逐套验证。
「趋势钝化下的区间空白」
趋势跟踪在零售交易者里普及率高,主要因为规则直观:认方向、跟突破。但近几年的外汇与贵金属盘面,波段在范围和持续时间上都明显压缩,纯趋势系统的触发频率下降,回撤期拉长。 横盘或窄幅振荡占掉相当比例的交易时间。若只做趋势,这部分窗口的潜在盈利就直接让渡给做市与高频策略。外汇和贵金属本身杠杆高、跳空频发,这种静默期流动性薄,假突破比例也会抬升,属于高风险盲区。 窄幅振荡的典型特征是价格被锁在极小区间,且能维持数日甚至数周。此时没有可用方向,挂单成交稀薄,靠均线或动量追单容易反复止损。开 MT5 调出 EURUSD 的 H1,对比 2023 与 2019 的 ATR(14) 中位数,就能直观看到区间收敛。
◍ 横盘策略要先厘清的两件事
做横盘策略之前,得先把任务拆清楚,思路和趋势跟踪很像,但对象换成了窄幅振荡。横盘本身没有严格定义,只能靠迹象识别:价格在一个范围内贴近上下边界来回,缺乏明显纵向趋势,同时成交量萎缩、参与者士气低落,从小笔成交和弱势波动里能看出来。 第一件事是辨别横盘是否存在。光看均线金叉死叉不够,得结合价格包络带和量能一起判断,否则容易把弱势趋势误认成横盘。 第二件事是单笔持仓的目标怎么定。横盘交易基本就是通道玩法:虚构出上下边界,价格触边界反弹时反向开仓。上半通道卖出赌它回下边界,下边界买入赌它回上边界,止盈挂在对边,止损可用固定点值或边界外推。外汇和贵金属这类品种波动跳点大,通道误判概率不低,实盘前建议在 MT5 用历史数据跑一遍边界宽度 sensitivity。
十种横盘通道策略的滤波器搭配
横盘行情里做边界反弹,核心不是画通道,而是给通道配一个能认出「真横盘」的滤波器。原文列了 10 套组合,全部在 H1 或更低周期跑,离场大多设为价格触达反向边界,只有 WPR+ADX 和 RSI直方图+横盘这两套用止盈止损收尾。 轨道线+MFI 是最直白的一对:价格碰下轨且 MFI 低于 20 才买,碰上轨且 MFI 高于 80 才卖。布林带+双 SMA(周期 4 和 8)则用快慢均线位置过滤,把均线周期调小能提高信号灵敏度。 WSO/WRO 通道配埃勒斯分形维度,适合任意周期,分形维度低于阈值说明行情碎、无趋势。通道交叉百分比配趋势范围时,Percent 参数跟周期绑定——小时图建议 20~30,设太高会导致信号过滥。 唐奇安通道+三位一体脉动只在 5~30 分钟图有效,更高周期里脉动曲线会锯齿化、横盘区滞后且太窄。ATR 通道+CCI 色阶允许价格短暂脱轨,只要 CCI 还在阈值之间,价格倾向回通道。 下面这段是策略 #1 的 EA 核心逻辑,用 MFI 和轨道线边界做判断: void OnTick() { //--- 检查以前由该 EA 开立的订单 if(!Trade.IsOpenedByMagic(Inp_MagicNum)) { //--- 获取数据进行计算 if(!GetIndValue()) return; //--- 如果有买入信号,则开单 if(BuySignal()) Trade.BuyPositionOpen(Symbol(),Inp_Lot,close[0]-Inp_StopLoss*_Point,tp,Inp_MagicNum,Inp_EaComment); //--- 如果有卖出信号,则开单 if(SellSignal()) Trade.SellPositionOpen(Symbol(),Inp_Lot,close[0]+Inp_StopLoss*_Point,tp,Inp_MagicNum,Inp_EaComment); } } //+------------------------------------------------------------------+
| // | 买入条件 |
|---|
//+------------------------------------------------------------------+ bool BuySignal() { if(mfi[0]<20 && env_low[0]>close[0]) { tp=env_high[0]; return true; } else return false; } 逐行看:OnTick 先查当前魔术码有无持仓,无则取指标值;BuySignal 里 mfi[0]<20 是超卖、env_low[0]>close[0] 是价格跌破下轨,同时满足才把止盈设到上轨并返 true。 外汇和贵金属杠杆高,横盘也可能被数据瞬破,上述组合都只是概率优势,开 MT5 用策略测试器挨个跑 H1 历史才能看清哪套适合你的品种。
class="type">void OnTick() { class=class="str">"cmt">//--- 检查以前由该 EA 开立的订单 if(!Trade.IsOpenedByMagic(Inp_MagicNum)) { class=class="str">"cmt">//--- 获取数据进行计算 if(!GetIndValue()) class="kw">return; class=class="str">"cmt">//--- 如果有买入信号,则开单 if(BuySignal()) Trade.BuyPositionOpen(Symbol(),Inp_Lot,close[class="num">0]-Inp_StopLoss*_Point,tp,Inp_MagicNum,Inp_EaComment); class=class="str">"cmt">//--- 如果有卖出信号,则开单 if(SellSignal()) Trade.SellPositionOpen(Symbol(),Inp_Lot,close[class="num">0]+Inp_StopLoss*_Point,tp,Inp_MagicNum,Inp_EaComment); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 买入条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool BuySignal() { if(mfi[class="num">0]<class="num">20 && env_low[class="num">0]>close[class="num">0]) { tp=env_high[class="num">0]; class="kw">return true; } else class="kw">return class="kw">false; }
「把信号函数接到 OnTick 主循环」
这套通道 + 动量的逻辑最终要在 OnTick 里跑起来。先判断当前 Magic 编号下是否已有本 EA 的持仓,没有才去取指标缓冲,避免重复开仓。 SellSignal 的核心是 MFI(0) 大于 80 且收盘价刺穿上轨 env_high[0],此时把止盈直接挂到 env_low[0]。外汇与贵金属杠杆高,刺穿 envelope 上轨后回落概率偏大,但极端趋势里也可能继续拉伸,需自测滑点。 GetIndValue 用 CopyBuffer 拉两组指标句柄和收盘价,任意一条返回 <=0 就放弃本轮计算;这是 MT5 里防句柄失效的常规写法,回测时若日志频繁报错多半是句柄没初始化。 BuySignal 则看慢线在快线之上且收盘价低于布林下轨 bb_low[0],止盈取 bb_up[0]。开仓时止损用 Inp_StopLoss*_Point 偏移,多空对称。把下面代码直接丢进 EA 模板就能在 MT5 里验证信号触发频率。
class="type">bool SellSignal() { if(mfi[class="num">0]>class="num">80 && env_high[class="num">0]<close[class="num">0]) { tp=env_low[class="num">0]; class="kw">return true; } else class="kw">return class="kw">false; } class="type">bool GetIndValue() { class="kw">return(CopyBuffer(InpInd_Handle1,class="num">0,class="num">0,class="num">2,mfi)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">1,class="num">0,class="num">2,env_low)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">0,class="num">0,class="num">2,env_high)<=class="num">0 || CopyClose(Symbol(),PERIOD_CURRENT,class="num">0,class="num">2,close)<=class="num">0 )?class="kw">false:true; } class="type">void OnTick() { if(!Trade.IsOpenedByMagic(Inp_MagicNum)) { if(!GetIndValue()) class="kw">return; if(BuySignal()) Trade.BuyPositionOpen(Symbol(),Inp_Lot,close[class="num">0]-Inp_StopLoss*_Point,tp,Inp_MagicNum,Inp_EaComment); if(SellSignal()) Trade.SellPositionOpen(Symbol(),Inp_Lot,close[class="num">0]+Inp_StopLoss*_Point,tp,Inp_MagicNum,Inp_EaComment); } } class="type">bool BuySignal() { if(ma_slow[class="num">0]>ma_fast[class="num">0] && bb_low[class="num">0]>close[class="num">0]) { tp=bb_up[class="num">0]; class="kw">return true; } else class="kw">return class="kw">false; }