自动构造支撑和阻力线·进阶篇
📐

自动构造支撑和阻力线·进阶篇

(2/3)·手画支撑阻力总被主观线干扰?这篇把ZigZag极点筛选和线有效性标准拆成可复用的代码逻辑

案例拆解新手友好 第 2/3 篇
很多交易者把ZigZag跑出来的每个拐点都连成线,结果图表被无意义斜线铺满,反而掩盖了真实供给区。极点值会随周期切换而漂移,不按趋势方向做筛选,阻力线在下跌里根本碰不到价格。

◍ 下跌趋势线的交叉判定与绘图落地

这段逻辑干的事很直接:在已框定的 a、b 两点区间内,倒序扫 K 线,统计收盘价由上穿下一条斜率为 coef 的基准线的次数,计数存进 cross_bc。当 cross_bc 同时不大于 Intersection_bc 与 Intersection_ab 时,才认为满足“下降通道未被破坏”的条件 2 和 3,进而把该段写进 DownTrend 矩阵。 矩阵字段填的是 a/b 柱索引、ax/bx 价格坐标、deviation 绝对值以及 coef 斜率,y 作为自增下标。这意味着同一轮扫描可能识别出多条互不冲突的下降段,全部暂存,不等绘图。 真正画线在后面独立循环里发生:遍历 DownTrend,要求 a、b 均大于 0 且间距非 0,才调用 TrendCreate 在图表上生成名为 “DownTrend j” 的斜线对象,随后 ChartRedraw 强制重绘。外汇与贵金属波动剧烈,这类自动趋势线仅描述已发生结构,后续破位概率随消息面显著变化,实盘须手动复核。 把下面代码直接丢进 MT5 脚本段,改 Intersection_bc / Intersection_ab 两个阈值,就能看不同宽松度下画出的下降线数量差异。

MQL5 / C++
for(class="type">int n=b-class="num">1; n>=class="num">0; n--)
  if(close[n]>(bx+coef*(b-n)) && close[n+class="num">1]<(bx+coef*(b-n+class="num">1)))
    cross_bc++;
  if(cross_bc<=Intersection_bc && cross_bc<=Intersection_ab)class=class="str">"cmt">// 定义是否满足条件 class="num">2 和 class="num">3
  {
    class=class="str">"cmt">// 填写下降趋势矩阵
    ArrayResize(DownTrend,y+class="num">1);
    DownTrend[y].a=a;
    DownTrend[y].b=b;
    DownTrend[y].ax=ax;
    DownTrend[y].bx=bx;
    DownTrend[y].dst=MathAbs(deviation);
    DownTrend[y].coef=coef;
    y++;
  }
class=class="str">"cmt">// 使用获得的矩阵显示图表上的下跌趋势线
for(j=class="num">0; j<ArraySize(DownTrend); j++)
{
  a=DownTrend[j].a;
  b=DownTrend[j].b;
  ax=DownTrend[j].ax;
  bx=DownTrend[j].bx;
  coef=DownTrend[j].coef;
  if(a>class="num">0 && b>class="num">0 && MathAbs(a-b)>class="num">0)
  {
    if(a>class="num">0 && b>class="num">0 && MathAbs(a-b)>class="num">0)
    {
      class=class="str">"cmt">//--- 创建趋势线 
      TrendCreate(class="num">0,"DownTrend "+class="type">class="kw">string(j),class="num">0,time[a],ax,time[b],bx,DColor,DStyle,DWidth,DBack,DSelection,DRayLeft,DRayRight,DHidden,DZOrder);
      ChartRedraw();
    }
  }
}

「顺着趋势在支撑阻力附近找进场点」

上升趋势里贴近支撑线买入、下跌趋势里贴近阻力线卖出,这是支撑阻力最基础的用法。横盘或形态整理阶段也一样适用,比如下跌转横盘后,阻力线附近卖出比支撑位买入成功概率更高;若向上趋势中出现三角形,则优先在三角形支撑线附近买。 价格靠近支撑阻力不代表一定会弹开,突破是常态。所以顺势交易前必须等确认:要么价格在该价位附近横住走稳,要么明确从该价位反弹再动手,避免假突破扫损。 从支撑线买入时,等价格突破整理区高点并回踩站稳再进,说明该位确实在牵引价格向上;阻力线卖出则反过来,等阻力区下方低点被跌破后开仓。 平仓要提前想好:支撑线买入的,在碰到大一级强阻力前了结,或次一级阻力位出场;若价格冲破该阻力,可持有博更大空间。例如三角形支撑买入后,持仓到价格突破三角形上沿续涨,再在下一条阻力线离场。外汇与贵金属杠杆高,假突破造成的连续扫损风险不可忽视。

指标趋势线驱动的 EA 三种交易逻辑

这套 EA 直接吃同图表上指标画出的趋势线,实时跟线操作。它本身带止损止盈、最大多空单数、尾随止损、盈亏平衡,以及反向信号平掉旧单的功能;高周期滤波器给了 MACD、RSI、WPR、MA 四种可选,EA 里的指标参数只用于回测,实盘以指标端配置为准。 开单逻辑分三档:mode 0 只在价格触碰趋势线后未破位、下一根收线仍守住时做回滚;mode 1 专做突破,要求刺穿线但收线未破、次根收线破位;mode 2 两种都吃。回测看,回滚单的盈利能力倾向低于突破单。 2015–2017 年 EURUSD H1 上,用 MACD 过滤(值小于 0 买、大于 0 卖),单方向每次只开 0.1 手,反向信号先平再反手。默认设置跑下来,只有 EURUSD 和 USDCHF 表现出尚可的结果——说明不能无脑参考所有趋势线,还要叠文章开头列的其他因子。 外汇和贵金属属高风险品种,支撑阻力线在重要新闻后常被秒破。实务上可给 EA 加新闻过滤器,或切到 RENKO 图表回避噪音。下面这段 signal() 是信号判定的核心,逐行拆一下。 int res=0 等是各滤波器与结果暂存;Use_macd 等开关为真才调对应函数拿信号。CopyOpen/High/Low/Close 取最近 3 根 K 线的开高低收进数组。ObjectsTotal 扫图表上所有 OBJ_TREND 对象,只认 UpTrend/DownTrend 前缀的线。 取线的两端价与时间,iBarShift 转成柱索引,kkk 算斜率,lvl/plvl 推当前与次根价位。mode 0 或 2 时:上升线且低刺破次根线、两根收线均站回线上给买信号 1;下降线对称给卖信号 2。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">int</span> signal()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">class="type">int</span> res=<span class="number">class="num">0</span>;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> macd=<span class="number">class="num">0</span>;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> rsi=<span class="number">class="num">0</span>;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> wpr=<span class="number">class="num">0</span>;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> ma=<span class="number">class="num">0</span>;
&nbsp;&nbsp; <span class="keyword">if</span>(Use_macd==<span class="macro">true</span>)macd=macdS();
&nbsp;&nbsp; <span class="keyword">if</span>(Use_rsi==<span class="macro">true</span>)rsi=rsiS();
&nbsp;&nbsp; <span class="keyword">if</span>(Use_wpr==<span class="macro">true</span>)wpr=wprS();
&nbsp;&nbsp; <span class="keyword">if</span>(Use_ma==<span class="macro">true</span>)ma=maS();
&nbsp;&nbsp; <span class="functions">CopyOpen</span>(<span class="macro">NULL</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,<span class="number">class="num">3</span>,O);
&nbsp;&nbsp; <span class="functions">CopyHigh</span>(<span class="macro">NULL</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,<span class="number">class="num">3</span>,H);
&nbsp;&nbsp; <span class="functions">CopyLow</span>(<span class="macro">NULL</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,<span class="number">class="num">3</span>,L);
&nbsp;&nbsp; <span class="functions">CopyClose</span>(<span class="macro">NULL</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,<span class="number">class="num">3</span>,C);
&nbsp;&nbsp; Signals=<span class="number">class="num">0</span>;
&nbsp;&nbsp; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=<span class="number">class="num">0</span>;i&lt;<span class="functions">ObjectsTotal</span>(<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="macro">OBJ_TREND</span>);i++)
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">string</span> sName=<span class="functions">ObjectName</span>(<span class="number">class="num">0</span>,i,<span class="number">class="num">0</span>,<span class="macro">OBJ_TREND</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(<span class="functions">StringFind</span>(sName,<span class="class="type">class="kw">string">"UpTrend"</span>)==<span class="number">class="num">0</span> || <span class="functions">StringFind</span>(sName,<span class="class="type">class="kw">string">"DownTrend"</span>)==<span class="number">class="num">0</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ax=<span class="functions">ObjectGetDouble</span>(<span class="number">class="num">0</span>,sName,<span class="macro">OBJPROP_PRICE</span>,<span class="number">class="num">0</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bx=<span class="functions">ObjectGetDouble</span>(<span class="number">class="num">0</span>,sName,<span class="macro">OBJPROP_PRICE</span>,<span class="number">class="num">1</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p1=(<span class="keyword">class="type">int</span>)<span class="functions">ObjectGetInteger</span>(<span class="number">class="num">0</span>,sName,<span class="macro">OBJPROP_TIME</span>,<span class="number">class="num">0</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p2=(<span class="keyword">class="type">int</span>)<span class="functions">ObjectGetInteger</span>(<span class="number">class="num">0</span>,sName,<span class="macro">OBJPROP_TIME</span>,<span class="number">class="num">1</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a=<span class="functions">iBarShift</span>(p1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b=<span class="functions">iBarShift</span>(p2);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kkk=(bx-ax)/(a-b);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lvl=bx+kkk*b;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; plvl=bx+kkk*(b-<span class="number">class="num">1</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">if</span>(mode==<span class="number">class="num">0</span> || mode==<span class="number">class="num">2</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(<span class="functions">StringFind</span>(sName,<span class="class="type">class="kw">string">"UpTrend"</span>)==<span class="number">class="num">0</span> &amp;&amp; L[<span class="number">class="num">1</span>]&lt;=plvl &amp;&amp; C[<span class="number">class="num">1</span>]&gt;plvl &amp;&amp; C[<span class="number">class="num">0</span>]&gt;lvl)Signals=<span class="number">class="num">1</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(<span class="functions">StringFind</span>(sName,<span class="class="type">class="kw">string">"DownTrend"</span>)==<span class="number">class="num">0</span> &amp;&amp; H[<span class="number">class="num">1</span>]&gt;=plvl &amp;&amp; C[<span class="number">class="num">1</span>]&lt;plvl &amp;&amp; C[<span class="number">class="num">0</span>]&lt;lvl)Signals=<span class="number">class="num">2</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

◍ 信号合成与参数入口的衔接

趋势字串匹配只是第一步,真正下单前还要过一道过滤器。下面这段逻辑里,mode 为 1 或 2 时才启用 ZigZag 趋势名识别:前一根 K 线低点刺穿 plvl 但收盘收回其上、当前收盘跌回 lvl 下方,判为 UpTrend 反转空信号(Signals=2);反之高点刺穿 plvl 后收破、当前收过 lvl,判为 DownTrend 反转多信号(Signals=1)。 信号值出来后并不立刻成交,而是要和 MACD、RSI、WPR、MA 四个子指标确认位对齐。任意一项 Use_xxx 设为 false 就跳过该项;全部对应上才把 res 置为 1 或 2 返回。这种「与」逻辑意味着默认全开时,漏掉一个指标就不会出单。 参数区直接决定风险敞口。LotSize 默认 0.1 标准手,Slippage 给 30 点,StopLoss 与 TakeProfit 均为 0 表示不挂止损止盈——外汇与贵金属波动剧烈,0 止损在实盘里可能瞬间扩大亏损,务必手动填点数。 ZigZag 的三参数 _ExtDepth=12、_ExtDeviation=5、_ExtBackstep=3 控制拐点灵敏度;_fibo=30 是扩展位比率(百分比),_tolerance=200 作冗余容差。复制下面 input 块到 EA 头部,改 _ExtDepth 到 8 或 20 能在 MT5 上直观比对拐点密度变化。

MQL5 / C++
if(mode==class="num">1 || mode==class="num">2)
  {
    if(StringFind(sName,"UpTrend")==class="num">0 && L[class="num">1]<=plvl && C[class="num">1]>plvl && C[class="num">0]<lvl)Signals=class="num">2;
    if(StringFind(sName,"DownTrend")==class="num">0 && H[class="num">1]>=plvl && C[class="num">1]<plvl && C[class="num">0]>lvl)Signals=class="num">1;
  }
  }
  }
 if(Signals==class="num">1
    &&(macd==class="num">1 || Use_macd==false)
    && (rsi==class="num">1 || Use_rsi==false)
    && (wpr==class="num">1 || Use_wpr==false)
    && (ma==class="num">1 || Use_ma==false))res=class="num">1;
 if(Signals==class="num">2
    &&(macd==class="num">2 || Use_macd==false)
    && (rsi==class="num">2 || Use_rsi==false)
    && (wpr==class="num">2 || Use_wpr==false)
    && (ma==class="num">2 || Use_ma==false))res=class="num">2;
 class="kw">return(res);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">class="kw">string s="-------------------------------------------"; class=class="str">"cmt">// 主设置
class="kw">input class="type">int Magic=class="num">12345;
class="kw">input class="type">class="kw">double LotSize=class="num">0.1;
class="kw">input class="type">int Slippage=class="num">30; class=class="str">"cmt">//滑点, 点数 
class="kw">input class="type">int StopLoss=class="num">0; class=class="str">"cmt">//止损, 点数 
class="kw">input class="type">int TakeProfit=class="num">0; class=class="str">"cmt">//止盈, 点数
class="kw">input class="type">int TrailingStart=class="num">0; class=class="str">"cmt">//为谁开始, 点数    
class="kw">input class="type">int TrailingStop= class="num">0; class=class="str">"cmt">//尾随停止, 点数    
class="kw">input class="type">int TrailingStep= class="num">0; class=class="str">"cmt">//尾随步长, 点数
class="kw">input class="type">int SL_prof=class="num">0; class=class="str">"cmt">//开始 BE, 点数
class="kw">input class="type">int SL_lev=class="num">0; class=class="str">"cmt">//BE 价位, 点数
class="kw">input class="type">int Buy_max=class="num">1; class=class="str">"cmt">//最大买单数量
class="kw">input class="type">int Sell_max=class="num">1; class=class="str">"cmt">//最大卖单数量
class="kw">input class="type">bool Sig_close=true; class=class="str">"cmt">//逆势交易平仓
class="kw">input tip mode=class="num">0;
class="kw">input class="type">class="kw">string s0="-------------------------------------------"; class=class="str">"cmt">// 指标设置
class="kw">input class="type">int _ExtDepth=class="num">12;
class="kw">input class="type">int _ExtDeviation=class="num">5;
class="kw">input class="type">int _ExtBackstep=class="num">3;
class="kw">input class="type">int _Min_dist=class="num">0;                                  class=class="str">"cmt">// 最小距离
class="kw">input class="type">int _fibo=class="num">30;                                     class=class="str">"cmt">// 菲波纳奇比率
class="kw">input class="type">int _tolerance=class="num">200;                               class=class="str">"cmt">// 冗余

「多指标滤波器的输入参数拆解」

这段输入声明把形态识别与指标过滤拆成了四组可独立调控的块,先在 MT5 里把 _Intersection_ab_Intersection_bc 都设为 1,意味着 a→b、b→c 两段只允许出现 1 个交汇点,过严会漏掉次优形态,过松则噪声激增。 MACD 块默认快线 12、慢线 26、信号 9,与经典参数一致;Use_macd=true 时它作为主滤波器参与信号判定。RSI 块周期 14,卖区设 70–100、买区设 0–30,但 Use_rsi=false 说明默认不启用,想叠加超买超卖约束时再手动开。 WPR 与 MA 块给出另一套阈值:WPR(14) 卖区 -20~0、买区 -100~-80,MA 则用周期 10 的收盘价 SMA 做趋势衬垫。外汇与贵金属波动属性不同,直接套同一组数可能放大假信号,建议先在 XAUUSD 的 M15 上只开 MACD 滤波回测,再逐步加 RSI 观察命中率变化。

MQL5 / C++
class="kw">input class="type">int _Intersection_ab=class="num">1;                           class=class="str">"cmt">// 从 a 点到 b 点允许的交汇点数量
class="kw">input class="type">int _Intersection_bc=class="num">1;                           class=class="str">"cmt">// 从 b 点到 c 点允许的交汇点数量
class="kw">input class="type">class="kw">string s1="-------------------------------------------";     class=class="str">"cmt">// MACD 设置
class="kw">input ENUM_TIMEFRAMES macd_tf=PERIOD_CURRENT;                      class=class="str">"cmt">// 周期
class="kw">input class="type">int fast_ema_period=class="num">12;                                      class=class="str">"cmt">// 快速均线周期 
class="kw">input class="type">int slow_ema_period=class="num">26;                                      class=class="str">"cmt">// 慢速均线周期 
class="kw">input class="type">int signal_period=class="num">9;                                         class=class="str">"cmt">// 差值的均化周期 
class="kw">input ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE;                class=class="str">"cmt">// 价格类型 
class="kw">input class="type">class="kw">string s2="-------------------------------------------";     class=class="str">"cmt">// RSI 设置
class="kw">input ENUM_TIMEFRAMES rsi_tf=PERIOD_CURRENT;                       class=class="str">"cmt">// 周期
class="kw">input class="type">int rsi_period=class="num">14;                                           class=class="str">"cmt">// 周期 
class="kw">input ENUM_APPLIED_PRICE rsi_applied_price=PRICE_CLOSE;            class=class="str">"cmt">// 价格类型
class="kw">input class="type">class="kw">double rsi_max_s=class="num">100;                                        class=class="str">"cmt">// 卖出的最大价格
class="kw">input class="type">class="kw">double rsi_min_s=class="num">70;                                         class=class="str">"cmt">// 卖出的最小价格
class="kw">input class="type">class="kw">double rsi_max_b=class="num">30;                                         class=class="str">"cmt">// 买入的最大价格
class="kw">input class="type">class="kw">double rsi_min_b=class="num">0;                                          class=class="str">"cmt">// 买入的最小价格
class="kw">input class="type">class="kw">string s3="-------------------------------------------";     class=class="str">"cmt">// WPR 设置
class="kw">input ENUM_TIMEFRAMES wpr_tf=PERIOD_CURRENT;                       class=class="str">"cmt">// 周期
class="kw">input class="type">int calc_period=class="num">14;                                          class=class="str">"cmt">// 周期  
class="kw">input class="type">class="kw">double wpr_max_s=class="num">0;                                          class=class="str">"cmt">// 卖出的最大价格
class="kw">input class="type">class="kw">double wpr_min_s=-class="num">20;                                        class=class="str">"cmt">// 卖出的最小价格
class="kw">input class="type">class="kw">double wpr_max_b=-class="num">80;                                        class=class="str">"cmt">// 买入的最大价格
class="kw">input class="type">class="kw">double wpr_min_b=-class="num">100;                                       class=class="str">"cmt">// 买入的最小价格
class="kw">input class="type">class="kw">string s4="-------------------------------------------";     class=class="str">"cmt">// MA 设置
class="kw">input ENUM_TIMEFRAMES ma_tf=PERIOD_CURRENT;                        class=class="str">"cmt">// 周期
class="kw">input class="type">int ma_period=class="num">10;                                            class=class="str">"cmt">// MA 周期
class="kw">input class="type">int ma_shift=class="num">0;                                              class=class="str">"cmt">// 平移 
class="kw">input ENUM_MA_METHOD ma_method=MODE_SMA;                           class=class="str">"cmt">// 平滑类型
class="kw">input ENUM_APPLIED_PRICE ma_applied_price=PRICE_CLOSE;             class=class="str">"cmt">// 价格类型 
class="kw">input class="type">bool Use_macd=true;                                          class=class="str">"cmt">// 使用 MACD 作为滤波器
class="kw">input class="type">bool Use_rsi=false;                                          class=class="str">"cmt">// 使用 RSI 作为滤波器
把极点排序交给小布
这些诊断小布盯盘的AIGC已内置,打开对应品种页即可看到自动标出的多周期极点簇,你只需核对斜率阈值,不用自己跑矩阵排序。

常见问题

之字折线的偏离参数按价格百分比计算,大周期波动绝对值更大,符合条件的拐点更少;同一参数跨周期复用会得到不同密度的极点集,需按分析期限取舍。
这样才能让连线斜率贴合下行通道上沿,若选更低的极点,线会穿进价格主体,失去阻力参照意义,变成无效对象。
可以,品种页的支撑阻力模块内置了菲波纳奇比率过滤,最小允许比率设0.25或0.382即可隐去短促噪声线,和文内标准一致。
穿越少说明线未被考验,属于年轻线;穿越多且仍守住,才是高相关位。建议结合BC段穿越与C点到现价距离综合排序,单看次数易清掉早期趋势线。
多在OnInit里iCustom参数拼写或缓冲索引错配,确认ZigZag指标名与终端安装名一致,且极点值缓冲号按指标源码核对后再读数组。