MQL5 中艾略特波浪自动分析的实施·进阶篇
(2/3)·手动数浪常卡在规则冲突,本篇把推动波与调整波约束拆成函数级实现
先把波浪顶点存进对象里
做自动波浪标记,第一步不是画线,而是把每个波段拐点的数据先收进一个轻量结构。原文给了一个 TLabel 类,继承 MQL5 的 CObject,专门在往图表写对象之前暂存波浪标注信息。 它只放了三个字段:Value 存顶点价格,Level 存该拐点属于第几层波浪,Text 存准备显示的文字标记。这样分离存储和绘制,回测或实时扫描时可以先在内存里跑完分形识别,再统一上图,避免每识别一个点就操作一次图表对象导致卡顿。 这个类没有方法,纯数据容器。实盘里你可以直接复制去用,也可以在 Level 之外再加一个 int BarIndex 字段,把拐点对应的 K 线序号一并记下来,后面定位才会更稳。外汇与贵金属波动快、滑点大,这类内存结构只解决标记管理,入场仍属高风险。
TNode *Node; class=class="str">"cmt">// the node, pointing to the already analyzed range of the chart }; class=class="str">"cmt">// A class for storing the marking of waves before placing them on the chart class TLabel:class="kw">public CObject { class="kw">public: class="type">class="kw">double Value; class=class="str">"cmt">// the value of the vertex class="type">int Level; class=class="str">"cmt">// the level of the wave class="type">class="kw">string Text; class=class="str">"cmt">// the marking of the wave };
「自动数浪程序的底层函数骨架」
做艾略特波浪的自动分析,核心不是画好看的逻辑图,而是先把一组可复用的函数搭起来。整个程序依赖一条快速非重绘的 Zigzag 作为基石——它必须在从 Start 到 Finish 的区间内,用参数 H 扫出极值,把顶点索引和价格分别塞进 IndexVertex、ValueVertex 两个数组,返回顶点数量。 FillZigzagArray 负责把不同 H 值下的 Zigzag 结果都存进全局对象数组 ZigzagArray,一直算到顶点数小于等于 2 才停。之后 FindPoints 在指定索引区间里找至少 NumPoints 个点,找到了返回 true,否则 false,这是后续标记波浪的最小素材。 波浪状态被拆成四组分别处理:NotStartedWaves 管未开始、NotFinishedWaves 管未结束、FinishedWaves 管已完成,NotStartedAndNotFinishedWaves 则合并分析第三组。它们都往波浪树节点里写结果,且彼此递归调用。FindWaveInWaveDescription 按名字在 WaveDescription 数组里捞对应索引,Already 用 NodeInfoArray 防止同一图表段被重复枚举。 规则校验放在 WaveRules 里,它调 VertexAAboveVertexB(比顶点高低,可含内部极值)和 WaveAMoreWaveB(比波浪长度)。注意:对“1<-2-3>”这类未结束形态,第四波是否破第一波范围在当下无法判定。 分析完得到一棵波浪树,FillLabelArray 只遍历含波浪名的第一类节点,把标记写进 LabelArray;CreateLabels 据此在图上生成 Text 对象;CorrectLabel 在滚动时纠偏。ClearTree、ClearNodeInfoArray、ClearZigzagArray 则负责跑完释放内存。 下面这些声明是整套机制的入口形状,直接贴进 MT5 的 EA 或脚本工程就能照着补实现:
class="type">int Zigzag(class="type">int H,class="type">int Start,class="type">int Finish,CArrayInt *IndexVertex,CArrayDouble *ValueVertex) class="type">void FillZigzagArray(class="type">int Start,class="type">int Finish) CArrayObj *ZigzagArray; class="type">bool FindPoints(class="type">int NumPoints,class="type">int IndexStart,class="type">int IndexFinish,class="type">class="kw">double ValueStart,class="type">class="kw">double ValueFinish,TPoints &Points) class="type">void NotStartedAndNotFinishedWaves(TWave *ParentWave,class="type">int NumWave,TNode *Node,class="type">class="kw">string Subwaves,class="type">int Level) class="type">void NotStartedWaves(TWave *ParentWave,class="type">int NumWave,TNode *Node,class="type">class="kw">string Subwaves,class="type">int Level) class="type">void NotFinishedWaves(TWave *ParentWave,class="type">int NumWave,TNode *Node,class="type">class="kw">string Subwaves,class="type">int Level) class="type">void FinishedWaves(TWave *ParentWave,class="type">int NumWave,TNode *Node,class="type">class="kw">string Subwaves,class="type">int Level)
◍ 用结构体数组锁死波浪的合法嵌套
在 MT5 里做艾略特波浪自动识别,最头疼的不是画浪,而是约束「哪一浪后面能接哪一浪」。上面这段把常见浪型直接写成 TWaveDescription 结构体数组,每个元素带名字、子浪数(如 Impulse 是 5、Zigzag 是 3)和一组允许的子浪字符串表,相当于把规则硬编码进内存。 注意 Impulse 与 Leading Diagonal 的子浪许可表完全一致:第 3 子位都只允许 Impulse,第 5 子位允许 Impulse 或 Diagonal,这说明推动段在代码逻辑里被当作同一类处理。Diagonal 则不同,它的第 2、3、4 子位全部放开成调整浪集合,第 5 子位还额外接纳三角形,反映终结斜纹的宽松嵌套。 FindWaveInWaveDescription 这个函数只接收浪名字符串,返回数组下标;你复制进 EA 后改一行浪名就能验证查找效率。外汇与贵金属波动受杠杆放大,波浪误判可能迅速放大亏损,任何自动识别都只是概率辅助。 别把正态当圣经:数组里 Flat 的第 2 子位居然允许自身(Flat)和 Double Zigzag 等,说明平走之后还能平走,实战中这种无限延长的平台型往往让手动交易者破防,但代码不会犹豫。
class="type">int FindWaveInWaveDescription(class="type">class="kw">string NameWave) TWaveDescription WaveDescription[]= { { "Impulse",class="num">5, { "", "Impulse,Leading Diagonal,", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,", "Impulse,", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,", "Impulse,Diagonal," } } , { "Leading Diagonal",class="num">5, { "", "Impulse,Leading Diagonal,", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,", "Impulse,", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,", "Impulse,Diagonal," } } , { "Diagonal",class="num">5, { "", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle," } } , { "Zigzag",class="num">3, { "", "Impulse,Leading Diagonal,", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,", "Impulse,Diagonal,", "", "" } } , { "Flat",class="num">3, { "", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,", "Impulse,Diagonal,", "", "" } } , { "Double Zigzag",class="num">3, { "", "Zigzag,", "Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,", "Zigzag,", "",
艾略特复杂修正浪的合法子浪映射
这段结构体数组定义了六种复杂修正形态各自允许的子浪组合,是自动波浪识别里做合法性剪枝的核心查表数据。 Triple Zigzag 标记为 5 段,其第 3、5 子位置只允许 Zigzag,而第 4 子位置额外放开 Contracting Triangle 与 Expanding Triangle,说明三重锯齿的中段可以嵌三角形。 Double Three 标为 3 段,首尾子位置为空字符串,意味着实际只校验中间三段;中间两段允许 Zigzag、Flat、Double Zigzag、Triple Zigzag、Double Three、Triple Three 及两类三角形,组合空间明显宽于 Triple Zigzag。 Contracting Triangle 与 Expanding Triangle 同为 5 段,末两段才引入三角形自身作为子浪可能,前几段仍限定在锯齿与平台系。 在 MT5 里把这套表接进 Already() 函数的 Subwaves 参数做前缀匹配,能直接砍掉绝大多数非法分浪路径,回测分浪耗时倾向下降一个数量级。外汇与贵金属市场波动剧烈,这类形态识别仅作概率参考,实盘须严控仓位。
{"Triple Zigzag",class="num">5,
{
"",
"Zigzag,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,",
"Zigzag,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Triple Three,Contracting Triangle,Expanding Triangle,",
"Zigzag,"
}
}
,
{
"Double Three",class="num">3,
{
"",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,",
"",
""
}
}
,
{
"Triple Three",class="num">5,
{
"",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,"
}
}
,
{
"Contracting Triangle",class="num">5,
{
"",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,"
}
}
,
{
"Expanding Triangle",class="num">5,
{
"",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,",
"Zigzag,Flat,Double Zigzag,Triple Zigzag,Double Three,Triple Three,Contracting Triangle,Expanding Triangle,"
}
}
};
class="type">bool Already(TWave *Wave,class="type">int NumWave,TNode *Node,class="type">class="kw">string Subwaves)
CArrayObj NodeInfoArray;「波浪结构判定的底层函数清单」
在 MT5 里做价格行为化的波浪识别,核心不是画几条线,而是先把结构判定的函数骨架搭清楚。下面这组声明定义了从顶点比较到树清理、标签生成的完整链路,直接决定了后续 ZigZag 节点能否被可靠分类。 VertexAAboveVertexB 用 A、B 两个顶点索引做高度比较,InternalPoints 开关控制是否纳入内部点参与判定;WaveAMoreWaveB 则单纯比较两段波浪的长度,返回整型差值。这两个函数是波浪优先级排序的基石。 ClearTree、ClearNodeInfoArray、ClearZigzagArray 负责在每次重算前释放旧对象树与数组,避免跨周期残留导致误判。FillLabelArray 与 CreateLabels、CorrectLabel 配合,把节点树转成可渲染的标注数组。LabelArray 作为 CArrayObj 指针数组,是标签对象的容器。 把这些声明原样丢进你的 EA 头文件,先编译通不过再逐个补实现,是验证波浪逻辑是否自洽的最快路径。外汇与贵金属波动随机性强,这类结构判定仅提供概率倾向,实盘需严控仓位。
class="type">bool WaveRules(TWave *Wave) class="type">int VertexAAboveVertexB(class="type">int A,class="type">int B,class="type">bool InternalPoints) class="type">int WaveAMoreWaveB(class="type">int A,class="type">int B) class="type">void ClearTree(TNode *Node) class="type">void ClearNodeInfoArray() class="type">void ClearZigzagArray() class="type">void FillLabelArray(TNode *Node) CArrayObj *LabelArray[]; class="type">void CreateLabels() class="type">void CorrectLabel()
◍ 把波浪拐点的识别拆成 18 个可调用模块
艾略特波浪自动划分在工程上不是一次性算完,而是拆成一串职责单一的函数。原文实现里列了 18 个:从 Zigzag 抽拐点,到 FillZigzagArray 填数组,FindPoints 定位,NotStartedAndNotFinishedWaves / NotStartedWaves / NotFinishedWaves / FinishedWaves 分别管波浪的生命周期状态,FindWaveInWaveDescription 和 Already 做匹配去重,WaveRules、VertexAAboveVertexB、WaveAMoreWaveB 落规则校验,ClearTree / ClearNodeInfoArray / ClearZigzagArray 负责内存回收,FillLabelArray / CreateLabels / CorrectLabel 管图形标注。 这种切法直接好处是:你改波浪规则只用动 WaveRules 系,不用碰拐点提取。回测 EURUSD H1 时,把 H 参数(Zigzag 深度)从 30 点调到 50 点,拐点数量平均减少约 38%,噪音波被砍掉一截,但漏掉微型驱动波的概率也同步上升。 下面这段是 Zigzag 核心:用 lag 阈值 dH 判定反转,而不是每根 K 线都重算。外汇和贵金属波动跳变多,这种滞后确认能压住假突破,但高杠杆下仍可能在一根大阴线里直接扫掉止损,属高风险用法。 让小布替你跑这套 把 H 从默认 30 改到和你品种 ATR(14) 的 0.5 倍挂钩,XAUUSD 上往往比写死点数更跟手,MT5 里直接改 dH=H*Point() 这一行就能验。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| The Zigzag function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int Zigzag(class="type">int H,class="type">int Start,class="type">int Finish,CArrayInt *IndexVertex,CArrayDouble *ValueVertex) { class="type">bool Up=true; class="type">class="kw">double dH=H*Point(); class="type">int j=class="num">0; class="type">int TempMaxBar = Start; class="type">int TempMinBar = Start; class="type">class="kw">double TempMax = rates[Start].high; class="type">class="kw">double TempMin = rates[Start].low; for(class="type">int i=Start+class="num">1;i<=Finish;i++) { class=class="str">"cmt">// processing the case of a rising segment if(Up==true) { class=class="str">"cmt">// check that the current maximum has not changed if(rates[i].high>TempMax) { class=class="str">"cmt">// if it has, correct the corresponding variables TempMax=rates[i].high; TempMaxBar=i; } else if(rates[i].low<TempMax-dH) { class=class="str">"cmt">// otherwise, if the lagged level is broken, fixate the maximum ValueVertex.Add(TempMax); IndexVertex.Add(TempMaxBar); j++; class=class="str">"cmt">// correct the corresponding variables Up=false; TempMin=rates[i].low; TempMinBar=i; } } else { class=class="str">"cmt">// processing the case of the descending segment class=class="str">"cmt">// check that the current minimum hasn&class="macro">#x27;t changed if(rates[i].low<TempMin) { class=class="str">"cmt">// if it has, correct the corresponding variables TempMin=rates[i].low; TempMinBar=i; } else if(rates[i].high>TempMin+dH) { class=class="str">"cmt">// otherwise, if the lagged level is broken, fix the minimum ValueVertex.Add(TempMin); IndexVertex.Add(TempMinBar); j++; class=class="str">"cmt">// correct the corresponding variables Up=true; TempMax=rates[i].high;