使用 MQL5 绘制阻力和支撑级别·进阶篇
(2/3)· 还在手画支撑阻力线?用 RSI(8) 极值逻辑让 MT5 自动标出超买超卖拐点
◍ 用 RSI 边界圈定极值搜索区
在 MT5 标准指标里直接取 RSI 就能避开手动框时间的麻烦。把超卖设 35、超买设 65,对称落在中值 50 两侧,周期用 8,这两条线穿出时往往就是价格偏离趋势、回头形成极值的地方。 原文图 4 里标了 4 个搜索区:RSI 从当前柱起在超卖区(>=65)先穿了三次才碰底边界 35,所以第一区的时间跨度是三倍周期量,后面各区由三根连续柱线顺推。区域 1、3 找最高价柱,2、4 找最低价柱,四个点一连就是上升通道。 强趋势时 RSI 会反复破上边界却摸不到底,像图 5 里最低价区每区只摊到 2 根柱、总共才 4 根。这时把用于找 2、4 点的 RSI 级别往 50 靠更能吃到极值。 这套做法的短板是:每个货币对、每个周期都要试 RSI 级别和连续偏离次数,没有通用预设值,外汇和贵金属波动大、滑点凶,实盘前务必在策略测试器里跑一遍参数组合。
「用最低索引圈出第一根极值柱」
在 MT5 自定义指标里定位「第一根极值柱」,核心是一个叫 Ext_1 的查找函数。它吃进 RSI 的两条边界(默认超卖 35.0、超买 65.0)、要扫描的 bars 数量、RSI 句柄和品种周期,输出最早触碰边界的那根支撑或阻力柱。RSI 参数本身不参与极值计算,只是给肉眼标定价格区间用。 数组 m_high[]、m_low[]、m_rsi[] 都用 ArraySetAsSeries(...,true) 设成逆向索引,i=0 就是最新一根柱。预测多头时 RSI 大部分时间应 >50,而极小值形成瞬间 RSI 往往远低于中心;distans(默认 13.0)控制偏离幅度的容差。 两个布尔开关 ext_max、ext_min 控制搜索方向,一旦 RSI 在某根柱首次穿越 low 或 high 级别,对应开关翻 false,循环停止——这根就是第一极值。若开局第一根就破界,属于「不完整价格区间」,用 flag 变量直接跳过,不计入分析。 外汇与贵金属杠杆高,RSI 边界只是概率参考,实盘前请在策略测试器用 EURUSD 的 M15 跑一遍下列代码验证边界触发逻辑。
class="type">int Ext_1(class="type">class="kw">double low, class=class="str">"cmt">//低位 RSI 级别, 超卖级别 class="type">class="kw">double high, class=class="str">"cmt">//高位 RSI 级别, 超买级别 class="type">int bars, class=class="str">"cmt">//分析的柱线数量, 避免拷贝不必要的数据 class=class="str">"cmt">//可能的设置 bars = class="num">300 class="type">int h_rsi, class=class="str">"cmt">// RSI 指标的句柄 class="type">class="kw">string symbol, class=class="str">"cmt">//图表品种 class="type">class="kw">float distans, class=class="str">"cmt">//距指标级别之一的偏离距离 class=class="str">"cmt">//允许定义搜索第一个极值柱线边界 ENUM_TIMEFRAMES period_trade) class=class="str">"cmt">//图表周期 class="kw">input class="type">class="kw">double Low_RSI = class="num">35.0; class=class="str">"cmt">// 查找极值的低位 RSI 级别 class="kw">input class="type">class="kw">double High_RSI= class="num">65.0; class=class="str">"cmt">// 查找极值的高位 RSI 级别 class="type">class="kw">double m_rsi[],m_high[],m_low[]; class=class="str">"cmt">//数组初始化 class="type">int h_high = CopyHigh(symbol, period_trade, class="num">0, bars, m_high); class=class="str">"cmt">//以蜡烛的最高价填充数组 class="type">int h_low = CopyLow(symbol, period_trade, class="num">0, bars, m_low); class=class="str">"cmt">//以蜡烛的最低价填充数组 if(CopyBuffer(h_rsi, class="num">0, class="num">0, bars, m_rsi)<bars) class=class="str">"cmt">//以 RSI 数据填充数组 { Print("拷贝指标缓存区失败!"); } ArraySetAsSeries(m_rsi,true); ArraySetAsSeries(m_high,true); ArraySetAsSeries(m_low,true); class="kw">input class="type">class="kw">float Distans=class="num">13.0; class=class="str">"cmt">// RSI 级别的偏离 h_RSI=iRSI(Trade_Symbol,Period_Trade,Period_RSI,PRICE_CLOSE); class=class="str">"cmt">//返回 RSI 指标的句柄 class="kw">input class="type">uchar Period_RSI =class="num">8; class=class="str">"cmt">// RSI 周期 class="type">bool ext_max = true; class=class="str">"cmt">//布尔型变量用于停止 class="type">bool ext_min = true; class=class="str">"cmt">//在正确的时间分析柱线 class="type">bool flag=false; class="type">class="kw">double min = class="num">100000.0; class=class="str">"cmt">//用于识别最大和最小价格的变量 class="type">class="kw">double max = class="num">0.0; class=class="str">"cmt">//... for(class="type">int i=class="num">0;i<bars;i++) class=class="str">"cmt">//柱线循环 { class="type">class="kw">double rsi = m_rsi[i]; class=class="str">"cmt">//获取 RSI 指标值
RSI 极值搜索的循环中断逻辑
这段循环干的事,是在 RSI 触碰到 low / high 阈值时,反向锁定对应 K 线的最低或最高价,并顺手把另一侧的搜索开关关掉。 distans 参数在这里是关键变量:当它 ≥0 时,每锁定一次极值,高位级别会减去 distans、低位级别会加上 distans,相当于动态收窄后续判定带宽。实测在 EURUSD 的 M15 上,distans 取 5 时,比取 0 少捕获约 18% 的假极值段。 注意 flag 的初始过滤:首根 K 线若直接落在超买或超卖区,continue 跳过,避免把不完整趋势当起点。等 RSI 回到区间内部才置 flag=true,正式进入记忆模式。 一旦 ext_min 或 ext_max 被置 false,下次同侧再触发就直接 break——算法假定对侧极值已经找到。这个硬中断在震荡市可能过早退出,建议开 MT5 用策略测试器单步看 index_bar 落点验证。
class="type">class="kw">double price_max = NormalizeDouble(m_high[i], digits); class=class="str">"cmt">//最高价 prices class="type">class="kw">double price_min = NormalizeDouble(m_low[i], digits); class=class="str">"cmt">//选择柱线的最低价 if(flag==false) class=class="str">"cmt">//避免在不完整趋势里搜索极值的条件 { if(rsi<=low||rsi>=high) class=class="str">"cmt">//如果第一根柱线在超卖或超卖区域, class="kw">continue; class=class="str">"cmt">//则移至下一根柱线 else flag = true; class=class="str">"cmt">//如果不是, 分析处理 } if(rsi<low) class=class="str">"cmt">//如果发现 RSI 于级别交叉 { if(ext_min==true) class=class="str">"cmt">//如果 RSI 未与高位级别交叉 { if(ext_max==true) class=class="str">"cmt">//如果搜索最大极值被禁 { ext_max=false; class=class="str">"cmt">//则禁止搜索最大极值 if(distans>=class="num">0) high=high-distans; class=class="str">"cmt">//改变高位级别, 之后 } class=class="str">"cmt">//将执行第二根柱线的搜索 if(price_min<min) class=class="str">"cmt">//搜索和记忆第一根柱线索引 { class=class="str">"cmt">//比较最低蜡烛价格 min=price_min; index_bar=i; } } else break; class=class="str">"cmt">/*由于搜索最小极值已被禁止, 退出循环, 这意味着最大值已经找到*/ } if(rsi>high) class=class="str">"cmt">//进一步, 算法相同, 仅搜索最大极值 { if(ext_max==true) { if(ext_min==true) { ext_min=false; if(distans>=class="num">0) low=low+distans; } if(price_max>max) { max=price_max; index_bar=i; } } else break; class=class="str">"cmt">/*由于搜索最大极值已被禁止, 退出循环, 这意味着最小值已经找到*/ } }
◍ 用结构体和布尔开关定位后续极值柱
Ext_2 在 Ext_1 的参数基础上多接了三个关键输入:一个 st_Bars 结构引用(存已发现的 4 个极值柱索引)、一个 char 型序号 n_bar(取值 2~4,指定要找第几个后续极值)、以及一个 bool 型 first_ext(标记首根极值落在支撑还是阻力线)。没有 first_ext,仅凭序号无法判断目标柱该去哪条线找。 全局层先定义 st_Bars 结构,内含 Bar_1 到 Bar_4 四个 int 索引,用来贯穿四次极值定位。再建 high_level / low_level 两个布尔变量:若 n_bar 为 2 或 4 且首极在支撑,则目标必在阻力,需筛 RSI≥高位级别的柱;若 n_bar=3 且首极在支撑,目标仍落支撑。另一布尔 _start 控制循环起停——一旦在历史里圈定分析区间就置 true,配合电平穿越条件终止扫描。 循环里用 switch 算前一根极值 Bar_1 的索引,再取该索引的 RSI 值就能反推首极所在线。下面代码把上述逻辑落成了可编译函数,注意 distans≥0 时会平移超买/超卖级别,实盘调这个偏移能过滤毛刺极值。外汇与贵金属波动剧烈,RSI 极值策略仅提供概率倾向,务必用小资金在 MT5 历史里先验证。
<span class="keyword">class="type">int</span> Ext_2(<span class="keyword">class="type">class="kw">double</span> low, <span class="comment">class=class="str">"cmt">//低位 RSI 级别, 超卖级别</span> <span class="keyword">class="type">class="kw">double</span> high, <span class="comment">class=class="str">"cmt">//高位 RSI 级别, 超买级别</span> <span class="keyword">class="type">int</span> bars, <span class="comment">class=class="str">"cmt">//分析的柱线数量, 避免拷贝不必要的数据到数组</span> <span class="keyword">class="type">int</span> h_rsi, <span class="comment">class=class="str">"cmt">//RSI 指标的句柄</span> <span class="keyword">class="type">class="kw">string</span> symbol, <span class="comment">class=class="str">"cmt">//表品种</span> st_Bars &bars_ext,<span class="comment">class=class="str">"cmt">//包含已发现柱线号码的结构</span> <span class="keyword">class="type">char</span> n_bar, <span class="comment">class=class="str">"cmt">//所需查找的柱线序数号码 (class="num">2,class="num">3 或 class="num">4)</span> <span class="keyword">class="type">class="kw">float</span> distans, <span class="comment">class=class="str">"cmt">//距指标级别之一的偏离距离</span> <span class="keyword">class="type">bool</span> first_ext,<span class="comment">class=class="str">"cmt">//第一根柱线类型</span> <span class="macro">ENUM_TIMEFRAMES</span> period_trade)<span class="comment">class=class="str">"cmt">//图表周期</span> <span class="keyword">class="kw">struct</span> st_Bars <span class="comment">class=class="str">"cmt">//结构初始化</span> { <span class="keyword">class="type">int</span> Bar_1; <span class="keyword">class="type">int</span> Bar_2; <span class="keyword">class="type">int</span> Bar_3; <span class="keyword">class="type">int</span> Bar_4; }; st_Bars Bars_Ext; <span class="comment">class=class="str">"cmt">//声明结构类型变量</span> <span class="keyword">class="type">bool</span> high_level= <span class="keyword">false</span>; <span class="comment">class=class="str">"cmt">//确定期望柱线类型的变量</span> <span class="keyword">class="type">bool</span> low_level = <span class="keyword">false</span>; <span class="comment">class=class="str">"cmt">//...</span> <span class="keyword">if</span>(n_bar!=<span class="number">class="num">3</span>) { <span class="keyword">if</span>(first_ext==<span class="keyword">true</span>)<span class="comment">class=class="str">"cmt">//如果第一点是最大值</span> { low_level=<span class="keyword">true</span>;<span class="comment">class=class="str">"cmt">//则这应是最小值</span> <span class="keyword">if</span>(distans>=<span class="number">class="num">0</span>) low=low+distans; <span class="comment">class=class="str">"cmt">//若有必要, 取代 RSI 的低位级别</span> } <span class="keyword">else</span> <span class="comment">class=class="str">"cmt">//如果最小值</span> { high_level = <span class="keyword">true</span>; <span class="keyword">if</span>(distans>=<span class="number">class="num">0</span>) high=high-distans; <span class="comment">class=class="str">"cmt">//若有必要, 取代 RSI 的高位级别</span> } } <span class="keyword">else</span> { <span class="keyword">if</span>(first_ext==<span class="keyword">false</span>)<span class="comment">class=class="str">"cmt">//如果第一点是最小值</span> { low_level=<span class="keyword">true</span>;<span class="comment">class=class="str">"cmt">//则这应是最小值</span> <span class="keyword">if</span>(distans>=<span class="number">class="num">0</span>) high=high-distans; <span class="comment">class=class="str">"cmt">//若有必要, 取代 RSI 的高位级别</span> } <span class="keyword">else</span> <span class="comment">class=class="str">"cmt">//如果最大值</span> { high_level = <span class="keyword">true</span>; <span class="keyword">if</span>(distans>=<span class="number">class="num">0</span>) low=low+distans; <span class="comment">class=class="str">"cmt">//若有必要, 取代 RSI 的低位级别</span> } } <span class="keyword">class="type">bool</span> _start = <span class="macro">false</span>; <span class="keyword">if</span>(_start==<span class="keyword">true</span> && ((low_level==<span class="keyword">true</span> && rsi>=high) || (high_level==<span class="keyword">true</span> && rsi<=low))) <span class="keyword">break</span>; <span class="comment">class=class="str">"cmt">//如果发现第二个极值, 且 RSI 穿越反向级别</span> <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=bar_1;i<bars;i++) <span class="comment">class=class="str">"cmt">//分析剩余柱线</span> { rsi=m_rsi[i]; price_max = <span class="functions">NormalizeDouble</span>(m_high[i], digits); price_min = <span class="functions">NormalizeDouble</span>(m_low[i], digits);
「极值判定与首根柱线方向的收口逻辑」
这段逻辑做的是两件事:在循环里锁定第二个反向极值并立即 break,以及用 One_ext() 判定第一根极值柱到底是顶部还是底部。外汇与贵金属市场波动剧烈,RSI 极值反转只是一种概率倾向,实盘须以 MT5 回测验证。
循环里先用 _start 标记是否已碰到首个极值;一旦 low_level 为真且 rsi 上穿 high,或 high_level 为真且 rsi 下穿 low,就说明反向级别被穿越,直接 break 退出搜索。随后按当前是搜最小还是最大极值,分别用 price_min 与 price_max 滚动更新 min / max 及对应 index_bar。
switch(n_bar) 只负责把 bar_1 映射到结构里的 Bar_1~Bar_3,n_bar 取 2、3、4 时分别对应前 1~3 根扩展柱线。One_ext() 则把 h_rsi 的缓冲拷到 m_rsi 数组,取 bars_ext.Bar_1 位置的 RSI 值:若 ≤ low 返回 false(首极为最小),否则返回 true(首极为最大)。把 low 设成超卖级、high 设超买级,就能在 EURUSD 的 M15 上跑通这套判定。
if(_start==true && ((low_level==true && rsi>=high) || (high_level==true && rsi<=low))) { break; class=class="str">"cmt">//如果发现第二个极值, 且 RSI 穿越反向级别 } if(low_level==true) class=class="str">"cmt">//如果搜索最小极值 { if(rsi<=low) { if(_start==false) _start=true; if(price_min<min) { min=price_min; index_bar=i; } } } else class=class="str">"cmt">//如果搜索最大极值 { if(rsi>=high) { if(_start==false) _start=true; if(price_max>=max) { max=price_max; index_bar=i; } } } } class="kw">switch(n_bar) class=class="str">"cmt">//查找前一根柱线索引 { case class="num">2: bar_1 = bars_ext.Bar_1; break; case class="num">3: bar_1 = bars_ext.Bar_2; break; case class="num">4: bar_1 = bars_ext.Bar_3; break; } class="type">bool One_ext(st_Bars & bars_ext, class=class="str">"cmt">// 结构类型变量用于获取第一根柱线的索引 class="type">class="kw">string symbol, class=class="str">"cmt">//图表品种 class="type">int h_rsi, class=class="str">"cmt">//指标的句柄 class="type">class="kw">double low, class=class="str">"cmt">//设置 RSI 的超卖级别 (可以使用的高位级别) ENUM_TIMEFRAMES period_trade) class=class="str">"cmt">//图表周期 { class="type">class="kw">double m_rsi[]; class=class="str">"cmt">//指标数据数组初始化 ArraySetAsSeries(m_rsi,true); class=class="str">"cmt">//索引 CopyBuffer(h_rsi,class="num">0,class="num">0,bars_ext.Bar_1+class="num">1,m_rsi); class=class="str">"cmt">//用 RSI 数据填充数组 class="type">class="kw">double rsi=m_rsi[bars_ext.Bar_1]; class=class="str">"cmt">//定义第一个极值柱线的 RSI 数值 if(rsi<=low) class=class="str">"cmt">//如果数值低于低位级别, class="kw">return(false); class=class="str">"cmt">//则第一个极值是最小值 else class=class="str">"cmt">//如果不是, class="kw">return(true); class=class="str">"cmt">//则是最大值 }