您应当知道的 MQL5 向导技术(第 56 部分):比尔·威廉姆斯(Bill Williams)分形(基础篇)
用向导把比尔·威廉姆斯分形挂上 EA
MQL5 向导里自带比尔·威廉姆斯分形信号模块,不写一行策略逻辑也能先跑出信号。它底层调用 iFractals,在每根 K 线判断左右各两根是否满足极值条件,属于纯价格形态过滤,不涉及任何均线或动量。 实际在 MT5 里新建 EA 时选「向导 → 信号 → Bill Williams Fractal」,编译后回测欧美 2024 年全年,分形翻转信号在 H1 上约每 120 根 K 线触发一次,信号稀疏但噪声低。 外汇与贵金属杠杆高,分形只是形态参考,信号出现后价格仍可能延续原趋势,务必用小仓位在策略测试器验证后再上实盘。
◍ 分形怎么标记逆转点
比尔·威廉姆斯的分形,是价格行为里识别局部逆转点的基础工具,核心是一个 5 根 K 线重复出现的形态。 当中间那根 K 线的最高价同时高于左右各两根的最高价,这个分形被定义为看跌分形,暗示上行段可能结束。反之中间 K 线最低价低于两侧四根的最低价,就是看涨分形,下行段可能反转。 在 MT5 里调出 Fractals 指标,肉眼就能验证:任意周期图表上,这类五柱形态都会自动标出上下箭头。外汇与贵金属波动大、假突破多,分形信号仅代表概率倾向,实战需配合其他过滤条件。
「连续同向下分形暴露的积累与阻力」
形态-0 的核心不在分形本身,而在「连续」。多头侧要求一连串分形低点出现在前低附近,且中间不能夹带分形高点——这和常规高低交替的分形序列不同,说明不同批次买家在相似价位接货,市场可能正处于上涨前的积累阶段。 空头侧则反过来:连续看跌分形高点若在大周期反复出现,往往映射出无法突破的阻力,卖压倾向持续,下跌趋势可能延续或开启。外汇与贵金属杠杆高,这类结构只作概率参考,不构成方向保证。 内置比尔·威廉姆斯分形指标直接调用常报无效值,所以改写了 FractalHigh / FractalLow 两个函数做边界判定。IsPattern_0 只查最近两根柱线是否连续出现同向下分形,想过滤假信号就把连续根数调大。 我们拿 GBPUSD 的 2024 年 H4 帧做前向测试,形态参数按 2023 年优化;结果显示形态-0 倾向提示趋势延续,已平仓后可能适合顺趋势重新进场。 代码逐行拆解: // IsPattern_0 按持仓方向查形态 bool CSignalFractals::IsPattern_0(ENUM_POSITION_TYPE T) { if(T == POSITION_TYPE_BUY && FractalLow(X()+1)!=0.0 && FractalLow(X())!=0.0) // 多单:当前与前一根都出有效分形低 { return(true); } // 命中看涨连续分形 else if(T == POSITION_TYPE_SELL && FractalHigh(X()+1)!=0.0 && FractalHigh(X())!=0.0) // 空单:连续分形高 { return(true); } return(false); // 否则不认形态 } double FractalHigh(int ind) // 高分形判定 { m_high.Refresh(-1); // 刷新缓冲 if(m_high.MaxIndex(ind,5)==ind) // 以5根窗口取极大索引 { return(m_high.GetData(ind)); } // 返回高点值 return(0.0); // 非分形返回0 } double FractalLow(int ind) // 低分形同理 { m_low.Refresh(-1); if(m_low.MinIndex(ind,5)==ind) // 5根窗口极小 { return(m_low.GetData(ind)); } return(0.0); }
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Check for Pattern class="num">0. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSignalFractals::IsPattern_0(class="type">ENUM_POSITION_TYPE T) { if(T == POSITION_TYPE_BUY && FractalLow(X() + class="num">1) != class="num">0.0 && FractalLow(X()) != class="num">0.0) { class="kw">return(true); } else if(T == POSITION_TYPE_SELL && FractalHigh(X() + class="num">1) != class="num">0.0 && FractalHigh(X()) != class="num">0.0) { class="kw">return(true); } class="kw">return(false); } class="type">class="kw">double FractalHigh(class="type">int ind) { class=class="str">"cmt">// m_high.Refresh(-class="num">1); if(m_high.MaxIndex(ind, class="num">5) == ind) { class="kw">return(m_high.GetData(ind)); } class="kw">return(class="num">0.0); } class="type">class="kw">double FractalLow(class="type">int ind) { class=class="str">"cmt">// m_low.Refresh(-class="num">1); if(m_low.MinIndex(ind, class="num">5) == ind) { class="kw">return(m_low.GetData(ind)); } class="kw">return(class="num">0.0); }
用分形高点被收破来抓逆势转折
看涨的分形形态-1,先得有一个曾经扮演阻力的看跌分形(即分形高点)。当价格逆着原方向冲上去,并把这根分形高点果断收破,买方压倒卖方的概率偏高,原阻力可能折返成支撑,暗示趋势逆转。若它处在更宽的看涨背景里,也可能是强势延续——这点在小周期图表上往往看不真切。 看跌版本正好反过来:先有看涨低点分形,随后价格收破该分形低点,视为卖方接手并为下行铺路。代码里只取上一根柱线的分形点,拿当前收盘价去比,逻辑直接不绕弯。 我们用 2023 年开盘与收盘阈值做参数优化,前瞻测试从 2024 年切入。结果上形态-1 比延续型的形态-0 更值得看,根子在于它能抓逆反。外汇与贵金属波动剧烈、杠杆风险高,实盘里最好把它和别的指标叠用,别单靠这一条信号开仓。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Check for Pattern class="num">1. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSignalFractals::IsPattern_1(class="type">ENUM_POSITION_TYPE T) { if(T == POSITION_TYPE_BUY && FractalHigh(X() + class="num">1) != class="num">0.0 && Close(X()) > FractalHigh(X() + class="num">1)) { class="kw">return(true); } else if(T == POSITION_TYPE_SELL && FractalLow(X() + class="num">1) != class="num">0.0 && Close(X()) < FractalLow(X() + class="num">1)) { class="kw">return(true); } class="kw">return(false); }
bool CSignalFractals::IsPattern_1(ENUM_POSITION_TYPE T) 定义成员函数,传入持仓方向 T,返回布尔。
if(T == POSITION_TYPE_BUY && FractalHigh(X() + 1) != 0.0 && Close(X()) > FractalHigh(X() + 1)) 多单情形:上一根柱线存在看跌分形高点,且当前收盘价大于该高点。
{ return(true); } 满足条件直接判为形态成立。
else if(T == POSITION_TYPE_SELL && FractalLow(X() + 1) != 0.0 && Close(X()) < FractalLow(X() + 1)) 空单对称:上一根有看涨分形低点,且收价跌破它。
{ return(true); } 同样返回成立。
return(false); 都不符则形态不成立。
开 MT5 把这段塞进 fractal 信号类,调 X() 的偏移就能验证不同周期的反应速度。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Check for Pattern class="num">1. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSignalFractals::IsPattern_1(class="type">ENUM_POSITION_TYPE T) { if(T == POSITION_TYPE_BUY && FractalHigh(X() + class="num">1) != class="num">0.0 && Close(X()) > FractalHigh(X() + class="num">1)) { class="kw">return(true); } else if(T == POSITION_TYPE_SELL && FractalLow(X() + class="num">1) != class="num">0.0 && Close(X()) < FractalLow(X() + class="num">1)) { class="kw">return(true); } class="kw">return(false); }
◍ 分形压缩区的突破确认与代码实现
看涨分形贴近前宽幅分形区间时,价格常在区内压缩,低位分形近乎拉成一条直线。价格站上这条窄带,买方脱离盘整,看涨倾向可能确立;反过来,看跌分形在先前区间内集群,是犹豫信号,跌破下沿则支撑失效,下行突破可能确认。 实现上用 CArrayDouble 装不定数量的分形点。因数组类没有标准差函数,且计算重,直接用分形点极差替代:把极差和收盘价10根 K 线的绝对变化比,更小就视为压缩成立。这只是粗略评估——趋势中收盘价波动大时,任何分形组都容易过测。 建议改成分形极差或标准差和某个绝对值比,再用优化精调。向导出的 EA 在 USDJPY、H4 上优化测试,形态-2 作为压缩形态显示出突破潜力;大周期通常分形更小,扩张前常先出现挤压。 下面这段代码是形态-2 的判断核心,逐行看:函数按多空类型分支,先循环把有效分形低/高点塞进 _buffer;再用 Maximum 减 Minimum 取极差,若小于等于 Close(X()) 与 Close(X()+10) 的绝对值差就返回 true。注意 FractalLow 返回 0.0 表示无分形,被跳过。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Check for Pattern class="num">2. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSignalFractals::IsPattern_2(class="type">ENUM_POSITION_TYPE T) { CArrayDouble _buffer; if(T == POSITION_TYPE_BUY) { for(class="type">int i = class="num">0; i < m_periods; i++) { if(FractalLow(X() + i) != class="num">0.0) { _buffer.Add(FractalLow(X() + i)); } } if(_buffer[_buffer.Maximum(class="num">0, _buffer.Total())] - _buffer[_buffer.Minimum(class="num">0, _buffer.Total())] <= fabs(Close(X()) - Close(X() + class="num">10))) { class="kw">return(true); } } else if(T == POSITION_TYPE_SELL) { for(class="type">int i = class="num">0; i < m_periods; i++) { if(FractalHigh(X() + i) != class="num">0.0) { _buffer.Add(FractalHigh(X() + i)); } } if(_buffer[_buffer.Maximum(class="num">0, _buffer.Total())] - _buffer[_buffer.Minimum(class="num">0, _buffer.Total())] <= fabs(Close(X()) - Close(X() + class="num">10))) { class="kw">return(true); } } class="kw">return(false); }