解读经典与隐性背离的新途径·进阶篇
(2/3)·多数背离策略卡在指标与价格的机械比对,本文给出供给需求视角的隐性识别路径
把价格新高和指标掉头划等号就开反向单,是背离交易里最容易被扫损的坏习惯。经典分类讲了十几年,但隐性背离为什么常在趋势延续里出现,很少被说清。这篇不重复教科书,直接从参与资金意愿切入看背离本质。
◍ 背离交易里那些容易踩的坑
背离形态只在趋势中存在,横盘阶段硬找背离基本是浪费时间。很多新手把震荡里的指标摆动当成信号,结果被来回扫损,这点必须先钉死。 除了指标层面的背离,入场还得靠价格行为或日本烛条确认。单看 MACD 或类似趋势指标的红绿柱背离就冲进去,胜率撑不住;尤其当价格靠近零轴交叉,信号往往被削弱甚至直接作废。 新闻行情里的蜡烛不能用来测背离。重大数据发布时波动骤增,烛条拉出极端影线,这类噪音生成的「背离」十有八九是假信号,回测里也经不起复盘。 背离要「明确」才有用——不光你自己看出来,其他市场参与者也得能认出同一结构,这种共识下决策正确的概率才会上移。另外只盯最近的波峰波谷,别拿半年前的老高点和现在比,那样只会把图表看乱。 外汇和贵金属杠杆高、滑点猛,背离出错是常态。任何信号都按常规仓位和止损规则走,别把概率优势当成确定性。
用加速器振荡指标挖非标准背离
标准背离规则之外,还有一条更靠前的路径:在比尔·威廉姆斯的加速器振荡指标(AO)上做文章。AO 的方向变化领先于驱动力,而驱动力又领先于价格,所以它天然是早一步的确认信号。代价是噪声会变多,但换来的是更密集的入场信号和更早的减速提示。 这种非标准背离仍沿用常用背离的框架,只是入场点的判定方式不按常规来。工具的核心目的就是抓背离,因此变量只留一个——用于判断背离的柱线数量,初始化成多色直方图。 可视化上,实线画经典背离,虚线画隐性背离;红色对应看跌、绿色对应看涨。粗线专门标出价格图上的最后隐性背离,以及指标窗内的经典背离。这样一眼能分清信号层级。 下面这段初始化代码里,indicator_buffers 设成 2,但 indicator_plots 只有 1——说明一个绘图层背后挂了数据和颜色两个缓冲区。DATA_LIMIT 宏定义为 37,意味着少于 37 根柱线直接不计算,避免边缘噪声。外汇与贵金属波动剧烈,这类早信号策略需先在 MT5 策略测试器里验证再上实盘。
class="macro">#class="kw">property indicator_separate_window class=class="str">"cmt">//---- 用于指标计算和绘制的一个缓冲区 class="macro">#class="kw">property indicator_buffers class="num">2 class=class="str">"cmt">//---- 只使用一个图形构造 class="macro">#class="kw">property indicator_plots class="num">1 class=class="str">"cmt">//---- 指标绘制为一条线 class="macro">#class="kw">property indicator_type1 DRAW_COLOR_HISTOGRAM class=class="str">"cmt">//---- 指标线使用蓝色 class="macro">#class="kw">property indicator_color1 Green,Red class=class="str">"cmt">//---- class="macro">#class="kw">property indicator_width1 class="num">2 class=class="str">"cmt">//---- 指标线是连续的曲线 class="macro">#class="kw">property indicator_style1 STYLE_SOLID class=class="str">"cmt">//---- 显示指标标签 class="macro">#class="kw">property indicator_label1 "AC_Div" class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">input class="type">int Bars_Calculated=class="num">1000; class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string shortname=""; class="type">class="kw">double AC_buff[]; class="type">class="kw">double Color_buff[]; class="type">int wid; class="type">int Handle_AC; class=class="str">"cmt">//--- class="macro">#define DATA_LIMIT class="num">37 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 自定义指标初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//---- 设置动态数组作为指标缓冲区 SetIndexBuffer(class="num">0,AC_buff,INDICATOR_DATA); SetIndexBuffer(class="num">1,Color_buff,INDICATOR_COLOR_INDEX); class=class="str">"cmt">//---- 设置指标绘图开始位置 PlotIndexSetInteger(class="num">1,PLOT_DRAW_BEGIN,DATA_LIMIT); class=class="str">"cmt">//---- 初始化指标简称的变量 shortname="Accelerator_Divergence"; class=class="str">"cmt">//---- 创建在单独子窗口和工具提示中显示的名称 IndicatorSetString(INDICATOR_SHORTNAME,shortname); class=class="str">"cmt">//---- 定义指标值的显示精度 IndicatorSetInteger(INDICATOR_DIGITS,_Digits); class=class="str">"cmt">//---- 禁用绘制空指标值 PlotIndexSetDouble(class="num">0, PLOT_EMPTY_VALUE, class="num">0.0); PlotIndexSetDouble(class="num">1, PLOT_EMPTY_VALUE, class="num">0.0); class=class="str">"cmt">//--- 加速器指标的句柄 Handle_AC=iAC(NULL,class="num">0); class=class="str">"cmt">//--- 查找图表子窗口的索引 wid=ChartWindowFind(class="num">0,shortname); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//---- 声明局部变量 class="type">int limit,bar,pos; class=class="str">"cmt">//---- 检查柱线数量是否足够进行计算 if(rates_total<DATA_LIMIT) class="kw">return(class="num">0); class="type">int barsCalculated=MathMin(Bars_Calculated,rates_total); class=class="str">"cmt">//+------- 设置数组索引方向 ---------------------+ ArraySetAsSeries(close,true); ArraySetAsSeries(AC_buff,true); ArraySetAsSeries(low,true); ArraySetAsSeries(high,true); ArraySetAsSeries(Color_buff,true); ArraySetAsSeries(time,true); class=class="str">"cmt">//+--- 确定计算所需的柱线数量 --------+ limit=rates_total-DATA_LIMIT-class="num">1; if(prev_calculated>class="num">0) limit=rates_total-prev_calculated; pos=limit; if(pos>barsCalculated)pos=limit; class="type">int to_copy; if(prev_calculated>rates_total || prev_calculated<class="num">0) to_copy=rates_total; else { to_copy=rates_total-prev_calculated;
「AC 背离的极值扫描与画线逻辑」
加速振荡器(AC)的背离识别,核心在于先定位局部极值,再比较价格与指标的斜率方向。下面这段实现用 Extremum() 判断三点形态:当 (a-b)*(b-c)<0 且 b 处于零轴下方、c 回抬时记为向下极值(返回 1),反之在零轴上方、c 走低时记为向上极值(返回 -1),其余情况返回 0。 LastPeak() 与 LastTrough() 从当前棒 l 向后跳 5 根再搜索,直到 bar-2 为止,分别抓第二个向上/向下极值;若找不到返回 -1。这种 +5 的偏移是为了避开邻近噪声棒,实盘里你可以把 5 改成 3 或 8 来适配不同周期波动。 主循环里,对向上极值 l 调用 LastPeak 拿到 counted:若 AC[i]<AC[counted] 而 high[i]>high[counted],就是价格新高、动能走弱的典型看跌背离,用红色实线连价格与指标;若反过来 AC 走高、高点降低,则画红色点线。向下背离对称处理,用绿色标记。外汇与贵金属杠杆高,背离仅提示概率转折,须配合止损。 CopyBuffer 取数前用 IsStopped() 拦截终止信号,避免 MT5 卸载指标时崩线程;着色循环里 Color_buff 默认 0,仅当 AC[bar]<AC[bar+1] 时置 1,方便前端做直方图红绿切换。
if(prev_calculated>class="num">0) to_copy++; class=class="str">"cmt">//--- if(IsStopped()) class="kw">return(class="num">0); class=class="str">"cmt">//Checking for stop flag class=class="str">"cmt">//+----- 主要数组 -------------------------------------+ if(CopyBuffer(Handle_AC,class="num">0,class="num">0,to_copy,AC_buff)<=class="num">0) { Print("getting Accelerator Handle is failed! Error",GetLastError()); class="kw">return(class="num">0); } class=class="str">"cmt">//+---------- 直方图着色 --------------------------------+ for(bar=limit; bar>=class="num">0 && !IsStopped(); bar--) { Color_buff[bar]=class="num">0.0; if(AC_buff[bar]<AC_buff[bar+class="num">1])Color_buff[bar] =class="num">1.0; if(AC_buff[bar]>AC_buff[bar+class="num">1])Color_buff[bar] =class="num">0.0; } class=class="str">"cmt">//+----------- 检测向上背离 ------------------------------+ class="type">int bars=barsCalculated; for(bar=pos; bar>=class="num">0 && !IsStopped(); bar--) { class="type">int l=bar+class="num">2; if(Extremum(AC_buff[l+class="num">1],AC_buff[l],AC_buff[l-class="num">1])<class="num">0) { class="type">int i=l; class="type">int counted=LastPeak(l,bars,AC_buff); if(counted!=-class="num">1) { if(AC_buff[i]<AC_buff[counted] && high[i]>high[counted]) { DrawPriceTrendLine(time[i],time[counted],high[i],high[counted],Red,STYLE_SOLID); DrawIndicatorTrendLine(time[i],time[counted],AC_buff[i],AC_buff[counted],Red,STYLE_SOLID); } if(AC_buff[i]>AC_buff[counted] && high[i]<high[counted]) { DrawPriceTrendLine(time[i],time[counted],high[i],high[counted],Red,STYLE_DOT); DrawIndicatorTrendLine(time[i],time[counted],AC_buff[i],AC_buff[counted],Red,STYLE_DOT); } } } class=class="str">"cmt">//+----------- 检测向下背离 ------------------------------+ if(Extremum(AC_buff[l+class="num">1],AC_buff[l],AC_buff[l-class="num">1])>class="num">0) { class="type">int i=l; class="type">int counted=LastTrough(l,bars,AC_buff); if(counted!=-class="num">1) { if(AC_buff[i]>AC_buff[counted] && low[i]<low[counted]) { DrawPriceTrendLine(time[i],time[counted],low[i],low[counted],Green,STYLE_SOLID); DrawIndicatorTrendLine(time[i],time[counted],AC_buff[i],AC_buff[counted],Green,STYLE_SOLID); } if(AC_buff[i]<AC_buff[counted] && low[i]>low[counted]) { DrawPriceTrendLine(time[i],time[counted],low[i],low[counted],Green,STYLE_DOT); DrawIndicatorTrendLine(time[i],time[counted],AC_buff[i],AC_buff[counted],Green,STYLE_DOT); } } } } class=class="str">"cmt">//+----- 搜索第二个向上极值 --------------------------+ class="type">int LastPeak(class="type">int l,class="type">int bar,class="type">class="kw">double &buf[]) { for(class="type">int i=l+class="num">5; i<bar-class="num">2; i++) if(Extremum(buf[i+class="num">1],buf[i],buf[i-class="num">1])<class="num">0)class="kw">return (i); class="kw">return (-class="num">1); } class=class="str">"cmt">//+----- 搜索第二个向下极值 --------------------------+ class="type">int LastTrough(class="type">int l,class="type">int bar,class="type">class="kw">double &buf[]) { for(class="type">int i=l+class="num">5; i<bar-class="num">2; i++) if(Extremum(buf[i+class="num">1],buf[i],buf[i-class="num">1])> class="num">0)class="kw">return (i); class="kw">return (-class="num">1); } class=class="str">"cmt">//+-- 搜索极值 -------------------------------------------+ class="type">int Extremum(class="type">class="kw">double a,class="type">class="kw">double b,class="type">class="kw">double c) { if((a-b)*(b-c)<class="num">0) { if(c>b && b<class="num">0) class="kw">return(class="num">1); class=class="str">"cmt">//DN extremum if(c<b && b>class="num">0) class="kw">return(-class="num">1);class=class="str">"cmt">//UP extremum } class="kw">return(class="num">0); } class=class="str">"cmt">//+------ 在价格图表上创建对象 -----------------------+
◍ 趋势线对象的创建与窗口分发
在 MT5 指标里画趋势线,核心不是算点,而是把点塞进正确的窗口并避免重名覆盖。上面这组函数把主图线和指标副图线拆成了两套入口,主图用 DrawPriceTrendLine,副图用 DrawIndicatorTrendLine,底层都交给 drawLineS 统一建对象。 DrawPriceTrendLine 先用 ObjectFind 查 name_2 是否存在,不存在才调 drawLineS 画实线;当 style 传 STYLE_DOT 时,额外画一条 clrAqua 的虚点线,宽度 3、向右射线开启。注意它用 shortname+DoubleToString(T_0,0) 当唯一名,靠时间戳避免重复。 DrawIndicatorTrendLine 多了一个 window=wid 参数,把线发到指标窗口。若 style==STYLE_SOLID,还会补一条 clrMagenta、宽度 2 的线,同样向右延伸。副图线的命名加了 wid 偏移,防止和主图同名冲突。 drawLineS 每次先 ObjectDelete 再 ObjectCreate,等于强制重画;OBJ_TREND 类型用 t0,p0 到 t1,p1 两点定位,OBJPROP_RAY_RIGHT 控制是否向右射线,OBJPROP_SELECTABLE 设 false 让线不可选中,back 决定压在主图下方还是上方。 开 MT5 新建指标把这段贴进 OnCalculate,把 wid 设成你指标窗口号,就能直接看到主副图双线联动;外汇和贵金属波动快,这类自动线在高波动时段可能频繁重绘,属正常现象,交易决策请自担高风险。
class="type">void DrawPriceTrendLine(class="type">class="kw">datetime T_0, class="type">class="kw">datetime T_1, class="type">class="kw">double P_0, class="type">class="kw">double P_1, class="type">class="kw">color color_0, class="type">int style) { class="type">class="kw">string name_2=shortname+DoubleToString(T_0,class="num">0); class="type">class="kw">string name_0; name_0=shortname+"Line_Sn"+ColorToString(color_0); class=class="str">"cmt">//--- if(ObjectFind(class="num">0,name_2)<class="num">0) drawLineS(name_2,T_0,T_1,P_0,P_1,color_0,style,class="num">0,true,false,class="num">0); class=class="str">"cmt">//+-----------+ if(style==STYLE_DOT) drawLineS(name_0,T_1,T_0,P_1,P_0,clrAqua,class="num">0,class="num">3,true,true,class="num">0); } class=class="str">"cmt">//+------ 在指标窗口中创建对象 ------------------+ class="type">void DrawIndicatorTrendLine(class="type">class="kw">datetime T_0, class="type">class="kw">datetime T_1, class="type">class="kw">double P_0, class="type">class="kw">double P_1, class="type">class="kw">color color_0, class="type">int style) { class="type">class="kw">string name_1,name_0; class="type">int window= wid; name_1 = shortname+DoubleToString(T_0+wid,class="num">0); if(ObjectFind(class="num">0,name_1)<class="num">0) drawLineS(name_1,T_0,T_1,P_0,P_1,color_0,style,class="num">0,false,false,window); class=class="str">"cmt">//--- if(style==STYLE_SOLID) { name_0=shortname+"Line_Pn"+ColorToString(color_0); drawLineS(name_0,T_1,T_0,P_1,P_0,clrMagenta,style,class="num">2,true,true,window); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void drawLineS(class="type">class="kw">string name, class="type">class="kw">datetime t0, class="type">class="kw">datetime t1, class="type">class="kw">double p0, class="type">class="kw">double p1, class="type">class="kw">color clr, class="type">int style, class="type">int width, class="type">bool back, class="type">bool ray, class="type">int window) { ObjectDelete(class="num">0,name); ObjectCreate(class="num">0,name,OBJ_TREND,window,t0,p0,t1,p1,class="num">0,class="num">0); ObjectSetInteger(class="num">0,name,OBJPROP_RAY_RIGHT,ray); ObjectSetInteger(class="num">0,name,OBJPROP_COLOR,clr); ObjectSetInteger(class="num">0,name,OBJPROP_STYLE,style); ObjectSetInteger(class="num">0,name,OBJPROP_WIDTH,width); ObjectSetInteger(class="num">0,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(class="num">0,name,OBJPROP_BACK,back); } class=class="str">"cmt">//+------------------------------------------------------------------+
背离延长线被突破后的角色互换
对价格图表与指标图形的实测观察显示,价格图上逆转背离的延长线,与指标图上经典背离的延长线,会自然形成倾斜的支撑与阻力带。当价格或柱状线有力突破这些倾斜线,往往意味着短线与长线走势可能出现方向延续。 一个具体现象是:价格图表中由隐性背离生成的支撑线被跌破后,后续反弹常在该线下方受阻;另一例中,MACD 直方图柱体直接击穿指标窗内的经典背离指示线,突破后该指示线由支撑转为反压。 这类线被有效突破后,原支撑变阻力、原阻力变支撑的概率偏高,外汇与贵金属市场因杠杆高、波动剧烈,此类互换失效也常见,需结合实时成交量验证。 基于这种角色互换特征,可把‘突破倾斜背离线+回踩不回破’作为开仓触发条件之一,具体参数留给下一阶段策略细化。