10 款趋势策略的比较分析·进阶篇
(2/3)· 从ADXCloud到Keltner通道,10套趋势系统的优劣与离场逻辑一次看透
用标准差和 RVI 卡住横盘突破
这套逻辑把「波动放大」和「动量方向」绑在一起才动手。横盘级别 FlatLevel 默认 10,趋势阈值 TrendLevel 取 Maximum,意味着只有标准差明显抬升、且快慢 RVI 分出方向时,才可能触发新单。 核心判定写在两个函数里:BuySignal 要求 stdev[0] 大于 trend 并且 rvi_fast[0] 大于 rvi_slow[0];SellSignal 则是 stdev[0] 大于 trend 但 rvi_fast[0] 小于 rvi_slow[0]。也就是说,光有波动率不够,必须快慢线也确认倾斜。 OnTick 先查 IsOpened 避免重复开仓,再调 GetIndValue 拉取三个缓冲区各 2 根柱的数据;只要任一 CopyBuffer 返回小于等于 0 就直接 return,不拿脏数据交易。外汇与贵金属杠杆高,这种条件单在震荡转趋势时可能生效,但假突破概率不低,建议先在 MT5 策略测试器跑 EURUSD 15M 验证。
class="kw">input class="type">int FlatLevel=class="num">10; class=class="str">"cmt">//横盘级别 class="kw">input Trend TrendLevel=Maximum; class=class="str">"cmt">//使用的趋势 class="type">void OnTick() { class=class="str">"cmt">//--- 检查 EA 以前的开单 if(!Trade.IsOpened(Inp_MagicNum)) { class=class="str">"cmt">//--- 获取数据进行计算 if(!GetIndValue()) class="kw">return; class=class="str">"cmt">//--- 如果是买入信号, 开单 if(BuySignal()) Trade.BuyPositionOpen(true,Symbol(),Inp_Lot,Inp_MMode,Inp_Deviation,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); class=class="str">"cmt">//--- 如果是卖出信号, 开单 if(SellSignal()) Trade.SellPositionOpen(true,Symbol(),Inp_Lot,Inp_MMode,Inp_Deviation,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 买入条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool BuySignal() { class="kw">return(stdev[class="num">0]>trend && rvi_fast[class="num">0]>rvi_slow[class="num">0])?true:class="kw">false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 卖出条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool SellSignal() { class="kw">return(stdev[class="num">0]>trend && rvi_fast[class="num">0]<rvi_slow[class="num">0])?true:class="kw">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,stdev)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">0,class="num">0,class="num">2,rvi_fast)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">1,class="num">0,class="num">2,rvi_slow)<=class="num">0 )?class="kw">false:true; }
「用快慢AC和布林能量过滤开仓」
这套逻辑只在当前无同魔法码持仓时才去拉指标、判信号,避免重复开单。OnTick 里先走 Trade.IsOpened(Inp_MagicNum) 判断,通不过就直接跳过,实盘里能少很多垃圾单。 买入要求快线 AC 在慢线 AC 上方,且 bb_power 当前值大于前一根,同时两根 bb_power 都为负。卖出反过来:快线低于慢线、bb_power 递减且两根都为正。这是一种在零轴下方找做多动能、零轴上方找做空动能的过滤思路。 GetIndValue 用 CopyBuffer 分别取 bb_power 2根、ac_fast 2根、ac_slow 2根,只要有任一缓冲区拷贝返回 ≤0 就放弃本轮计算。MT5 上若自定义指标句柄无效,这里会直接 return,不会误开仓。 外汇与贵金属杠杆高,这类信号只是概率倾向,真要跑先把 Inp_StopLoss、Inp_TakeProfit 按品种波动调好,再去策略测试器用 2023 年 XAUUSD 的 M15 数据回测一遍看成交密度。
class="type">bool BuySignal() { class="kw">return(ac_fast[class="num">0]>ac_slow[class="num">0] && bb_power[class="num">0]>bb_power[class="num">1] && (bb_power[class="num">0]<class="num">0 && bb_power[class="num">1]<class="num">0))?true:class="kw">false; } class="type">bool SellSignal() { class="kw">return(ac_fast[class="num">0]<ac_slow[class="num">0] && bb_power[class="num">0]<bb_power[class="num">1] && (bb_power[class="num">0]>class="num">0 && bb_power[class="num">1]>class="num">0))?true:class="kw">false; } class="type">bool GetIndValue() { class="kw">return(CopyBuffer(InpInd_Handle1,class="num">0,class="num">0,class="num">2,bb_power)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">0,class="num">0,class="num">2,ac_fast)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">1,class="num">0,class="num">2,ac_slow)<=class="num">0 )?class="kw">false:true; } class="type">void OnTick() { if(!Trade.IsOpened(Inp_MagicNum)) { if(!GetIndValue()) class="kw">return; if(BuySignal()) Trade.BuyPositionOpen(true,Symbol(),Inp_Lot,Inp_MMode,Inp_Deviation,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); if(SellSignal()) Trade.SellPositionOpen(true,Symbol(),Inp_Lot,Inp_MMode,Inp_Deviation,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); } }
◍ 用重心与速度过滤假突破
这套 EA 的逻辑把重心指标(cog)和平均速度(avr_speed)绑死:只有当 avr_speed[0] 大于 Trend_lev、且前后两根重心都在零轴同侧并发生转向时,才吐出买卖信号。比如 BuySignal 要求 cog[1]<cog[0] 且两者皆为负,意味着重心在零轴下方拐头向上,配合速度过阈,倾向判定为空头动能衰减后的反弹。 GetIndValue 负责把两个指标的近 2 根缓冲拉进数组,只要任一 CopyBuffer 返回值 ≤0 就直接 false,避免取到空数据瞎开仓。OnTick 里先查 IsOpened 防重复下单,再调信号函数,分别走 BuyPositionOpen / SellPositionOpen,参数里的 StopLoss、TakeProfit 由外部输入决定。 后面补的另一种写法更朴素:wave[0]>0 且 wave[1]<0 即买,wave[0]<0 且 wave[1]>0 即卖,相当于看单根穿越零轴。外汇与贵金属杠杆高,这类信号在震荡市可能频繁挨耳光,上 MT5 用策略测试器跑 EURUSD 的 M15 先验证触发频率再谈实盘。
class="type">bool BuySignal() { class="kw">return(avr_speed[class="num">0]>Trend_lev && cog[class="num">1]<cog[class="num">0] &&(cog[class="num">1]<class="num">0 && cog[class="num">0]<class="num">0))?true:class="kw">false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 卖出条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool SellSignal() { class="kw">return(avr_speed[class="num">0]>Trend_lev && cog[class="num">1]>cog[class="num">0] &&(cog[class="num">1]>class="num">0 && cog[class="num">0]>class="num">0))?true:class="kw">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,cog)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">0,class="num">0,class="num">2,avr_speed)<=class="num">0 )?class="kw">false:true); } class="type">void OnTick() { class=class="str">"cmt">//--- 检查 EA 以前的开单 if(!Trade.IsOpened(Inp_MagicNum)) { class=class="str">"cmt">//--- 获取数据进行计算 if(!GetIndValue()) class="kw">return; class=class="str">"cmt">//--- 如果是买入信号, 开单 if(BuySignal()) Trade.BuyPositionOpen(true,Symbol(),Inp_Lot,Inp_MMode,Inp_Deviation,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); class=class="str">"cmt">//--- 如果是卖出信号, 开单 if(SellSignal()) Trade.SellPositionOpen(true,Symbol(),Inp_Lot,Inp_MMode,Inp_Deviation,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 买入条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool BuySignal() { class="kw">return(wave[class="num">0]>class="num">0 && wave[class="num">1]<class="num">0)?true:class="kw">false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 卖出条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool SellSignal() { class="kw">return(wave[class="num">0]<class="num">0 && wave[class="num">1]>class="num">0)?true:class="kw">false; }
信号判定与指标取数的底层写法
EA 只在当前魔法码无持仓时才去读指标并试图开单,这是避免重复加仓的第一道闸门。OnTick 里先调 Trade.IsOpened 做存在性检查,再走 GetIndValue 拉数据,任一环节失败直接 return,不浪费后续计算。 取数函数有两版。第一版只 CopyBuffer 一个句柄的 0 号缓冲、取 2 根柱,逻辑过于单薄;第二版补齐了四个序列:指标1的 6 号缓冲进 obj_q3_b、2 号缓冲进 obj_q3_s,指标2的 0 号缓冲进 wo,再用 CopyClose 取最新 2 根收盘价进 close。只要其中任一 Copy 返回 ≤0,整体视为取数失败。 买卖信号都建立在『波浪方向 + 价格突破分位线』的双条件上。BuySignal 要求 wo[0]>wo[1] 且 close[0]>obj_q3_b[0],即动量转上且收盘价站上上分位;SellSignal 反之,wo[0]<wo[1] 且 close[0]<obj_q3_s[0]。外汇与贵金属波动剧烈,这类条件只提高概率,不保证方向。 把下面这段代码直接贴进 MT5 的 EA 源文件,改 InpInd_Handle1/2 为你实际加载的指标句柄,就能在策略测试器里验证信号触发频率。
class="type">bool GetIndValue() { class="kw">return(CopyBuffer(InpInd_Handle,class="num">0,class="num">0,class="num">2,wave)<=class="num">0)?class="kw">false:true; } class="type">void OnTick() { class=class="str">"cmt">//--- 检查 EA 以前的开单 if(!Trade.IsOpened(Inp_MagicNum)) { class=class="str">"cmt">//--- 获取数据进行计算 if(!GetIndValue()) class="kw">return; class=class="str">"cmt">//--- 如果是买入信号, 开单 if(BuySignal()) Trade.BuyPositionOpen(true,Symbol(),Inp_Lot,Inp_MMode,Inp_Deviation,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); class=class="str">"cmt">//--- 如果是卖出信号, 开单 if(SellSignal()) Trade.SellPositionOpen(true,Symbol(),Inp_Lot,Inp_MMode,Inp_Deviation,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); } } class="type">bool BuySignal() { class="kw">return(wo[class="num">0]>wo[class="num">1] && close[class="num">0]>obj_q3_b[class="num">0])?true:class="kw">false; } class="type">bool SellSignal() { class="kw">return(wo[class="num">0]<wo[class="num">1] && close[class="num">0]<obj_q3_s[class="num">0])?true:class="kw">false; } class="type">bool GetIndValue() { class="kw">return(CopyBuffer(InpInd_Handle1,class="num">6,class="num">0,class="num">2,obj_q3_b)<=class="num">0 || CopyBuffer(InpInd_Handle1,class="num">2,class="num">0,class="num">2,obj_q3_s)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">0,class="num">0,class="num">2,wo)<=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() { class=class="str">"cmt">//--- 检查 EA 以前的开单 if(!Trade.IsOpened(Inp_MagicNum)) {
「双指标过滤的开单判定逻辑」
这段逻辑把通道类和均线类指标叠在一起做入场筛选:快线穿慢线只给方向,价格突破通道边界才确认信号,避免在无趋势区间频繁碰单。外汇与贵金属杠杆高,这种组合在震荡市仍可能产生连续假信号,实盘前建议用 MT5 策略测试器跑至少 3 个月 tick 数据。 主流程先调 GetIndValue() 拉取四个缓冲区与收盘价,任一 Copy 失败就直接 return,防止用空数组计算。随后 BuySignal() 与 SellSignal() 各自独立判断,互不排斥但同根 K 线只会触发一边。 BuySignal 要求快线大于慢线且收盘价大于上轨,SellSignal 反之。注意两者都只看 [0] 当前柱,未做跨柱确认,因此信号可能在收盘前抖动——想降频可在调用处加一根柱收盘判定。 GetIndValue 里 CopyBuffer 的 2 代表取最近两根,但函数只用了 [0],[1] 是为后续做斜率预留。若你只做当前柱策略,把 2 改成 1 能省一点开销。
class=class="str">"cmt">//--- 获取数据进行计算 if(!GetIndValue()) class="kw">return; class=class="str">"cmt">//--- 如果是买入信号, 开单 if(BuySignal()) Trade.BuyPositionOpen(true,Symbol(),Inp_Lot,Inp_MMode,Inp_Deviation,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); class=class="str">"cmt">//--- 如果是卖出信号, 开单 if(SellSignal()) Trade.SellPositionOpen(true,Symbol(),Inp_Lot,Inp_MMode,Inp_Deviation,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 买入条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool BuySignal() { class="kw">return(cao_fast[class="num">0]>cao_slow[class="num">0] && close[class="num">0]>dcs_up[class="num">0])?true:class="kw">false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 卖出条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool SellSignal() { class="kw">return(cao_fast[class="num">0]<cao_slow[class="num">0] && close[class="num">0]<dcs_low[class="num">0])?true:class="kw">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,dcs_up)<=class="num">0 || CopyBuffer(InpInd_Handle1,class="num">1,class="num">0,class="num">2,dcs_low)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">0,class="num">0,class="num">2,cao_fast)<=class="num">0 || CopyBuffer(InpInd_Handle2,class="num">1,class="num">0,class="num">2,cao_slow)<=class="num">0 || CopyClose(Symbol(),PERIOD_CURRENT,class="num">0,class="num">2,close)<=class="num">0 )?class="kw">false:true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 专家交易系统输入参数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">input class="type">class="kw">string Inp_EaComment="Strategy #class="num">8"; class=class="str">"cmt">//EA 注释
◍ 把参数和开单守卫钉进 EA 骨架
下面这段 input 区块定义了策略可调的全部外部变量,直接决定 Schaff 趋势周期 EA 的下单行为与风控边界。手数固定 0.01,止损 400 点、止盈 600 点,偏离容差 20 点,属于偏宽幅的贵金属/外汇波段设定,实盘前应在 MT5 策略测试器按品种点值重算保证金占用。 超买 80、超卖 20 沿用了经典震荡通道,但 Schaff 趋势周期本身已做 MACD 式平滑,所以这两个阈值在 20/30 快慢均线组合下触发频率会低于裸 RSI。直方图平滑用 SMMA、快线 20、慢线 30、平滑相位 100、收盘价计算、随机周期 10,这组参数若改快,信号会前移但假突破概率倾向升高。 OnTick 里的 IsOpened 守卫是关键:同一 MagicNum 1111 下只允许一笔持仓,避免重复追单。外汇与贵金属杠杆高,这类无加仓逻辑的骨架在极端跳空时仍可能触发止损,验证时建议先开模拟盘跑一周再谈参数微调。
class="kw">input class="type">class="kw">double Inp_Lot=class="num">0.01; class=class="str">"cmt">//手数 class="kw">input MarginMode Inp_MMode=LOT; class=class="str">"cmt">//保证金模式 class="kw">input class="type">int Inp_MagicNum=class="num">1111; class=class="str">"cmt">//魔幻数字 class="kw">input class="type">int Inp_StopLoss=class="num">400; class=class="str">"cmt">//止损(点数) class="kw">input class="type">int Inp_TakeProfit=class="num">600; class=class="str">"cmt">//止盈(点数) class="kw">input class="type">int Inp_Deviation = class="num">20; class=class="str">"cmt">//偏离(点数) class="kw">input class="type">class="kw">double Overbuying=class="num">80; class=class="str">"cmt">//超买区域 class="kw">input class="type">class="kw">double Overselling=class="num">20; class=class="str">"cmt">//超卖区域 class=class="str">"cmt">//--- Schaff 趋势周期指标参数 class="kw">input Smooth_Method MA_SMethod=MODE_SMMA_; class=class="str">"cmt">//直方图平滑方法 class="kw">input class="type">int Fast_XMA = class="num">20; class=class="str">"cmt">//快速均线周期 class="kw">input class="type">int Slow_XMA = class="num">30; class=class="str">"cmt">//慢速均线周期 class="kw">input class="type">int SmPhase= class="num">100; class=class="str">"cmt">//均线平滑参数 class="kw">input Applied_price_ AppliedPrice=PRICE_CLOSE_; class=class="str">"cmt">//应用价格 class="kw">input class="type">int Cycle=class="num">10; class=class="str">"cmt">//随机振荡器周期 class="type">void OnTick() { class=class="str">"cmt">//--- 检查 EA 以前的开单 if(!Trade.IsOpened(Inp_MagicNum)) { class=class="str">"cmt">//--- 获取数据进行计算