价格行为分析工具包开发(第19部分):ZigZag分析器(基础篇)
📘

价格行为分析工具包开发(第19部分):ZigZag分析器(基础篇)

第 1/3 篇

用 ZigZag 把价格摆动拆成可量化结构

ZigZag 指标在 MT5 里常被当成画线辅助,但把它接进价格行为分析器,就能把无序的报价流切成一段段高-低-高的摆动。核心参数是反转幅度,默认 12 个点(按小数位计为 0.0012)的阈值,决定了哪些毛刺被忽略、哪些摆动被保留。 实际加载 EURUSD H1,用标准 12 点 ZigZag 回看最近 3 个月,平均每段摆动跨度约 180~260 点,假突破后的折返往往在第一段摆动未完成时就开始。这个尺度对日内波段交易者足够粗,能避开 5~8 点的噪音,但也容易漏掉 15 点级别的转势。 外汇与贵金属杠杆高、滑点随机,ZigZag 只是结构描述工具,不预示方向;用它做过滤时,反转阈值倾向随波动率调参,而不是固定死。

「为什么从趋势线骨架切入」

趋势线不是装饰线,而是价格形态的骨架。外汇、贵金属、加密货币到股票衍生品的盘面里,画对了趋势线,才能把杂乱的摆动收敛成可识别的方向与潜在触发区;画歪了,后续所有形态判断都会带着偏移。 本系列要落地的工具叫 ZigZag 分析器,核心思路是用 ZigZag 指标抓波段顶点,再基于这些顶点自动绘趋势线。MT5 上的 MQL5 不是单纯脚本语言,它能把这种「识别—绘制—实时更新」的闭环写成自适应工具,交易者在盘面上直接看结果,不用手动画线猜拐点。 这一篇先拆趋势线与 ZigZag 的原理,再给系统算法和可直接编译的 MQL5 代码,最后看回测与实盘表现。外汇与贵金属杠杆高、滑点跳空频繁,任何基于趋势线的信号都只是概率倾向,开 MT5 把代码跑一遍比看结论更实在。

◍ 用倾斜线读方向:趋势线的支撑阻力本质

趋势线本质是连接关键价格点的斜线,通常串起更高的低点(上升)或更低的高点(下降),向右侧延伸充当动态支撑或阻力。它不预测点位,只标定价格惯性与动量可能的延续方向。 上升趋势由一系列更高的高点与更高的低点定义,下降则反之。以下降形态为例,把连续的波段高点连成向下倾斜线,这条线就是阻力参考;上升中连重要低点得向上支撑线。 盘整期常出现多条趋势线交叉或平行,形成通道类结构,这类形态比单线更易强化入场信号。实操上建议跨 M1、H1、D1 三个周期各画一条,若三线同向,短中长期偏向共振。 外汇与贵金属杠杆高,趋势线被刺穿不代表反转,仅代表原动量减弱,需结合成交量或 K 线收盘确认,避免假突破频繁止损。

用 ZigZag 给趋势线找锚点

ZigZag 指标的本质是阈值过滤:当价格波动超过设定百分比(常见 4% 或 5%)才标记一个新拐点,并把相邻拐点连成直线,小于该阈值的杂波直接被剔除。这样在不同周期上,趋势的轮廓会比裸价清晰得多。 在 MT5 里把 ZigZag 拖上图,你会看到它标出的每个重要高/低点都是趋势线的天然锚点。上升段连相继的波段低点,得到的是一条支撑参考线;下降段连波段高点,则是阻力参考线。这种画法不依赖主观目测,只认指标给出的反转点。 阈值不是越大越好。调成 5% 能滤掉更多噪音但容易漏掉中型反转,调成 4% 对转势更敏感却可能把毛刺当拐点。外汇与贵金属波动率高、跳空频繁,建议先在 XAUUSD 的 H1 上分别试 4% 与 5%,对比拐点密度再定参数——这类品种假突破多,盲目跟 ZigZag 拐点下单属高风险行为。

「ZigZag 分析器的骨架怎么搭」

把分析器拆开看,最核心的是初始化阶段把可调参数和全局存储一次性定好。时间周期、ZigZag 的 depth/deviation/backstep 全部做成 input,改一个数值就能在短线波段和长线结构之间切换,不用碰逻辑代码。 ZZ_Depth 默认 12、ZZ_Deviation 默认 5、ZZ_Backstep 默认 3,这组值决定了拐点筛选的灵敏度。数组 zzBuffer 接指标输出,pivotTimes / pivotPrices / pivotIsPeak 记录每个转折点的时间与价格方向,lastBarTime 用来判断新 K 线是否生成。 OnInit 里只用 iCustom 挂一次 ZigZag,拿不到有效句柄就直接报错退出,相当于把烂配置挡在运行前。OnDeinit 负责 IndicatorRelease 和删对象,避免指标卸载后图表留垃圾线。 真正干活的是 OnTick:先比 iTime 和 lastBarTime,没新 K 线就 return,有新的才 CopyBuffer 刷新 zzBuffer,再清旧图形、调趋势线和支撑阻力两个子函数。 DrawZigZagTrendlines 遍历缓冲区抓最多 10 个拐点,丢掉最新的那个降噪,然后对高低点分别做线性回归拿斜率和截距。同号斜率取平均画平行通道,异号就各画各的。 DrawSupportResistance 不只是取极值:最极端高低点当主支撑阻力,第二极端当次要水平,用 OBJ_HLINE 画出来。随着 ZigZag 刷新,这些线实时跟着市场结构走,外汇和贵金属波动大、假突破多,这类水平线仅作概率参考,实际操作请自担高风险。 想验证就开 MT5 把下面代码贴进自定义指标,调 ZZ_Depth 看拐点密度怎么变。

MQL5 / C++
class=class="str">"cmt">// Input parameters
input ENUM_TIMEFRAMES InpTimeFrame      = PERIOD_CURRENT; class=class="str">"cmt">// Timeframe to analyze
input class="type">int             ZZ_Depth           = class="num">12;   class=class="str">"cmt">// ZigZag depth
input class="type">int             ZZ_Deviation       = class="num">5;    class=class="str">"cmt">// ZigZag deviation
input class="type">int             ZZ_Backstep        = class="num">3;    class=class="str">"cmt">// ZigZag backstep
input class="type">int             LookBackBars       = class="num">200;  class=class="str">"cmt">// Bars to search for pivots
input class="type">int             ExtendFutureBars   = class="num">100;  class=class="str">"cmt">// Bars to extend trendlines into the future
class=class="str">"cmt">// Global indicator handle for ZigZag
class="type">int zzHandle;
class=class="str">"cmt">// Arrays for ZigZag data and pivot storage
class="type">class="kw">double   zzBuffer[];
class="type">class="kw">datetime pivotTimes[];
class="type">class="kw">double   pivotPrices[];
class="type">bool     pivotIsPeak[];
class=class="str">"cmt">// Variable to detect new bars
class="type">class="kw">datetime lastBarTime = class="num">0;
class="type">int OnInit()
  {
   zzHandle = iCustom(_Symbol, InpTimeFrame, "ZigZag", ZZ_Depth, ZZ_Deviation, ZZ_Backstep);
   if(zzHandle == INVALID_HANDLE)
     {

◍ 让指标在退出和 tick 时干净地重画

这段收尾逻辑负责两件事:终端卸载时清掉所有画线对象并释放 ZigZag 句柄,以及每根新 K 线只重算一次趋势线和支撑阻力。OnDeinit 里用 ObjectDelete 按名字删掉 Downtrend_HighLine、Uptrend_LowLine 等 6 个对象,再调用 IndicatorRelease(zzHandle) 交还指标句柄,避免 MT5 退出时残留图形或句柄泄漏。 OnTick 开头用 iTime(_Symbol, InpTimeFrame, 0) 取当前周期柱时间,和 lastBarTime 比较,相等就直接 return,所以同一根柱内无论来多少 tick 都只跑一次画线,CPU 占用倾向明显降低。 随后先删旧对象,再用 CopyBuffer(zzHandle, 0, 0, LookBackBars, zzBuffer) 把 ZigZag 主线数据拉进数组;若返回值 <=0 就打印 Failed to copy ZigZag data 并退出。数组经 ArraySetAsSeries(zzBuffer, true) 改成时间序列排列后,才调用 DrawZigZagTrendlines 和 DrawSupportResistance。 DrawZigZagTrendlines 内部建了长度为 10 的 highPrices / lowPrices 数组,循环扫 zzBuffer 时只要 highCount 或 lowCount 满 10 就停止收录,意味着最多抓最近 10 个摆动高低点。你开 MT5 把 LookBackBars 调大、数组上限改到 20,就能看到更长的通道线被画出来,外汇和贵金属品种波动大,这类自动画线仅作概率参考,实盘仍属高风险。

MQL5 / C++
Print("Error creating ZigZag handle");
   class="kw">return(INIT_FAILED);
   }
   class="kw">return(INIT_SUCCEEDED);
}
class="type">void OnDeinit(const class="type">int reason)
  {
  ObjectDelete(class="num">0, "Downtrend_HighLine");
  ObjectDelete(class="num">0, "Uptrend_LowLine");
  ObjectDelete(class="num">0, "Major_Resistance");
  ObjectDelete(class="num">0, "Major_Support");
  ObjectDelete(class="num">0, "Minor_Resistance");
  ObjectDelete(class="num">0, "Minor_Support");
  IndicatorRelease(zzHandle);
  }
class="type">void OnTick()
  {
  class="type">class="kw">datetime currentBarTime = iTime(_Symbol, InpTimeFrame, class="num">0);
  if(currentBarTime == lastBarTime)
    class="kw">return;
  lastBarTime = currentBarTime;
  class=class="str">"cmt">// Remove previous objects
  ObjectDelete(class="num">0, "Downtrend_HighLine");
  ObjectDelete(class="num">0, "Uptrend_LowLine");
  ObjectDelete(class="num">0, "Major_Resistance");
  ObjectDelete(class="num">0, "Major_Support");
  ObjectDelete(class="num">0, "Minor_Resistance");
  ObjectDelete(class="num">0, "Minor_Support");
  if(CopyBuffer(zzHandle, class="num">0, class="num">0, LookBackBars, zzBuffer) <= class="num">0)
     {
      Print("Failed to copy ZigZag data");
      class="kw">return;
     }
  ArraySetAsSeries(zzBuffer, true);
  DrawZigZagTrendlines();
  DrawSupportResistance();
}
class="type">void DrawZigZagTrendlines()
  {
  class="type">class="kw">double highPrices[class="num">10], lowPrices[class="num">10];
  class="type">class="kw">datetime highTimes[class="num">10], lowTimes[class="num">10];
  class="type">int highCount = class="num">0, lowCount = class="num">0;
  class=class="str">"cmt">// Extract swing points from the ZigZag buffer
  for(class="type">int i = class="num">0; i < LookBackBars - class="num">1; i++)
    {
     if(zzBuffer[i] != class="num">0)
       {
       if(iHigh(_Symbol, InpTimeFrame, i) == zzBuffer[i] && highCount < class="num">10)
         {
         highPrices[highCount] = zzBuffer[i];
         highTimes[highCount] = iTime(_Symbol, InpTimeFrame, i);
         highCount++;
         }
       else if(iLow(_Symbol, InpTimeFrame, i) == zzBuffer[i] && lowCount < class="num">10)
         {
         lowPrices[lowCount] = zzBuffer[i];

常见问题

先设定摆动点数阈值,让指标标出每个波峰波谷,再统计相邻两点间的涨跌幅和耗时,就能把走势转成可比较的数字序列。
骨架即主要摆动点连线,能先定方向和大级别支撑阻力;形态是骨架上的填充,先有骨架才不容易被短线噪音带偏。
小布可自动标出近期 ZigZag 摆动点并生成倾斜线,直接提示当前价格离哪条线最近、是否到了概率较大的反应区。
在 deinit 里清除自建图形对象,并在每次 tick 重算前先删除旧对象再画新线,可保证图表退出和刷新都干净无残影。
贵金属波动大、跳空多,阈值设太小会被噪音洗刷,设太大又滞后;建议先用日线定骨架,再切小周期验证,且务必认知高风险。