您应该了解的MQL5向导技巧(第七十部分):结合指数核网络使用SAR与RVI形态(基础篇)
◍ 用向导把 SAR 与 RVI 形态喂给指数核网络
MT5 的 MQL5 向导能省掉手写信号类的麻烦,这次演示的是把 SAR 反转点和 RVI 摆动形态一起送进指数核网络做分类。思路不复杂:SAR 管趋势转向,RVI 管动能强弱,两者叠加后让核网络判断当前属于哪一类市况。 实际在向导里选信号组时,把 SAR 和 RVI 同时勾上,再指定指数核网络作为训练后端,系统会按历史样本拟合权重。外汇与贵金属杠杆高,信号失效时回撤可能很快,任何形态组合都只是概率倾向,不是方向保证。 验证方法很直接:生成 EA 后丢进策略测试器,用 2020–2024 的 XAUUSD H1 数据跑样本外,观察核网络在 SAR 假突破段的误判率,再决定是否调 RVI 的周期参数。
用映射值唤醒被弃用的 SAR/RVI 形态
上一节给出的 SAR 与 RVI 组合共测了 10 种形态,编号 0 到 9,其中索引 1、2、6 在前进测试里失效,被暂时关掉。EA 端用 PatternsUsed 参数按 2 的幂调用:索引 1 对应 2^1=2,索引 2 对应 2^2=4,索引 6 对应 2^6=64;10 种全开就是 2^10-1=1023。0–1023 之间不是纯幂的值代表多形态叠加,可自行让 EA 混跑,但本系列先不展开。 这一篇要做的,是用监督学习把失效的形态 1、2、6 重新训活。实现上选 Python 写模型与训练——即便没 GPU,开发和数据吞吐也够快。连行情走 MetaTrader 5 的 Python 模块,填账号密码就能直连券商 MT 服务器。 连上后券商历史价就能拉回来。Python 技术指标库大多要另装且格式别扭,SAR 和 RVI 从公式手搓并不难,所以这一步先在 Python 里复刻这两个指标,为后面接模型做准备。外汇和贵金属波动剧烈,任何形态重构都只是提高信号概率,实盘前请在 MT5 用历史数据回测验证。
「用 Python 复刻抛物转向的止损跟踪逻辑」
抛物转向(SAR)属于趋势跟踪类工具,靠点位落在价格一侧来提示趋势可能反转的位置。多头时 SAR 在波段低点下方,空头时在波段高点上方,本质上就是一条随趋势加速移动的止损参考线。 下面这段函数接收含 high、low、close 列的 pandas 数据帧,以及加速因子 af 与上限 af_max,默认 0.02 和 0.2,输出时把算好的 SAR 作为新列塞回原表。先 copy 一份避免改坏原始数据,再把 high、low 提出来当 NumPy 数组跑循环,比直接操 pandas 序列快。 初始化很关键:趋势先假定多头(trend=1),极值点 ep 取首根最高价,首期 SAR 取首根最低价,加速因子从 af 起步。这样 SAR 在多头起点就天然躺在价格下方,和它本身做止损位的角色一致。 循环里每根 K 线先按 sar = 上一 sar + af_current*(ep - 上一 sar) 向极值点靠拢。若多头中当期最低价跌破 SAR,就翻空:SAR 重置为上期 ep,ep 改当期最低价,af 归位。若延续多头,则 SAR 不超过前两期最低价较小值,且新高刷新 ep 时 af 按步长递增但封顶 af_max。
<span class="keyword">def</span> SAR(df: pd.DataFrame, af: <span class="built_in">class="type">class="kw">float</span> = <span class="number">class="num">0.02</span>, af_max: <span class="built_in">class="type">class="kw">float</span> = <span class="number">class="num">0.2</span>) -> pd.DataFrame: <span class="class="type">class="kw">string">""" Calculate Parabolic SAR indicator and append it as &class="macro">#x27;SAR&class="macro">#x27; column to the input DataFrame. Args: df(pd.DataFrame): DataFrame with &class="macro">#x27;high&class="macro">#x27;, &class="macro">#x27;low&class="macro">#x27;, &class="macro">#x27;close&class="macro">#x27; columns af(class="type">class="kw">float): Acceleration factor, class="kw">default class="num">0.02 af_max(class="type">class="kw">float): Maximum acceleration factor, class="kw">default class="num">0.2 Returns: pd.DataFrame: Input DataFrame with new &class="macro">#x27;SAR&class="macro">#x27; column """</span> <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">all</span>(col <span class="keyword">in</span> df.columns <span class="keyword">for</span> col <span class="keyword">in</span> [<span class="class="type">class="kw">string">&class="macro">#x27;high&class="macro">#x27;</span>, <span class="class="type">class="kw">string">&class="macro">#x27;low&class="macro">#x27;</span>, <span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>]): <span class="keyword">raise</span> ValueError(<span class="class="type">class="kw">string">"DataFrame must contain &class="macro">#x27;high&class="macro">#x27;, &class="macro">#x27;low&class="macro">#x27;, &class="macro">#x27;close&class="macro">#x27; columns"</span>) <span class="keyword">if</span> af <= <span class="number">class="num">0</span> <span class="keyword">or</span> af_max <= <span class="number">class="num">0</span> <span class="keyword">or</span> af > af_max: <span class="keyword">raise</span> ValueError(<span class="class="type">class="kw">string">"Invalid acceleration factors"</span>) <span class="keyword">if</span> df.empty: <span class="keyword">raise</span> ValueError(<span class="class="type">class="kw">string">"DataFrame is empty"</span>) result_df = df.copy() result_df[<span class="class="type">class="kw">string">&class="macro">#x27;SAR&class="macro">#x27;</span>] = <span class="number">class="num">0.0</span> sar = df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>].iloc[<span class="number">class="num">0</span>] ep = df[<span class="class="type">class="kw">string">&class="macro">#x27;high&class="macro">#x27;</span>].iloc[<span class="number">class="num">0</span>] af_current = af trend = <span class="number">class="num">1</span> <span class="keyword">if</span> <span class="built_in">len</span>(df) > <span class="number">class="num">1</span> <span class="keyword">and</span> df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>].iloc[<span class="number">class="num">1</span>] > df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>].iloc[<span class="number">class="num">0</span>] <span class="keyword">else</span> -<span class="number">class="num">1</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">class="num">1</span>, <span class="built_in">len</span>(df)): prev_sar = sar high, low = df[<span class="class="type">class="kw">string">&class="macro">#x27;high&class="macro">#x27;</span>].iloc[i], df[<span class="class="type">class="kw">string">&class="macro">#x27;low&class="macro">#x27;</span>].iloc[i] <span class="keyword">if</span> trend > <span class="number">class="num">0</span>: sar = prev_sar + af_current * (ep - prev_sar) <span class="keyword">if</span> low < sar: trend = -<span class="number">class="num">1</span> sar = ep ep = low af_current = af <span class="keyword">else</span>:
<span class="keyword">def</span> SAR(df: pd.DataFrame, af: <span class="built_in">class="type">class="kw">float</span> = <span class="number">class="num">0.02</span>, af_max: <span class="built_in">class="type">class="kw">float</span> = <span class="number">class="num">0.2</span>) -> pd.DataFrame: <span class="class="type">class="kw">string">""" Calculate Parabolic SAR indicator and append it as &class="macro">#x27;SAR&class="macro">#x27; column to the input DataFrame. Args: df(pd.DataFrame): DataFrame with &class="macro">#x27;high&class="macro">#x27;, &class="macro">#x27;low&class="macro">#x27;, &class="macro">#x27;close&class="macro">#x27; columns af(class="type">class="kw">float): Acceleration factor, class="kw">default class="num">0.02 af_max(class="type">class="kw">float): Maximum acceleration factor, class="kw">default class="num">0.2 Returns: pd.DataFrame: Input DataFrame with new &class="macro">#x27;SAR&class="macro">#x27; column """</span> <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">all</span>(col <span class="keyword">in</span> df.columns <span class="keyword">for</span> col <span class="keyword">in</span> [<span class="class="type">class="kw">string">&class="macro">#x27;high&class="macro">#x27;</span>, <span class="class="type">class="kw">string">&class="macro">#x27;low&class="macro">#x27;</span>, <span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>]): <span class="keyword">raise</span> ValueError(<span class="class="type">class="kw">string">"DataFrame must contain &class="macro">#x27;high&class="macro">#x27;, &class="macro">#x27;low&class="macro">#x27;, &class="macro">#x27;close&class="macro">#x27; columns"</span>) <span class="keyword">if</span> af <= <span class="number">class="num">0</span> <span class="keyword">or</span> af_max <= <span class="number">class="num">0</span> <span class="keyword">or</span> af > af_max: <span class="keyword">raise</span> ValueError(<span class="class="type">class="kw">string">"Invalid acceleration factors"</span>) <span class="keyword">if</span> df.empty: <span class="keyword">raise</span> ValueError(<span class="class="type">class="kw">string">"DataFrame is empty"</span>) result_df = df.copy() result_df[<span class="class="type">class="kw">string">&class="macro">#x27;SAR&class="macro">#x27;</span>] = <span class="number">class="num">0.0</span> sar = df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>].iloc[<span class="number">class="num">0</span>] ep = df[<span class="class="type">class="kw">string">&class="macro">#x27;high&class="macro">#x27;</span>].iloc[<span class="number">class="num">0</span>] af_current = af trend = <span class="number">class="num">1</span> <span class="keyword">if</span> <span class="built_in">len</span>(df) > <span class="number">class="num">1</span> <span class="keyword">and</span> df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>].iloc[<span class="number">class="num">1</span>] > df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>].iloc[<span class="number">class="num">0</span>] <span class="keyword">else</span> -<span class="number">class="num">1</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">class="num">1</span>, <span class="built_in">len</span>(df)): prev_sar = sar high, low = df[<span class="class="type">class="kw">string">&class="macro">#x27;high&class="macro">#x27;</span>].iloc[i], df[<span class="class="type">class="kw">string">&class="macro">#x27;low&class="macro">#x27;</span>].iloc[i] <span class="keyword">if</span> trend > <span class="number">class="num">0</span>: sar = prev_sar + af_current * (ep - prev_sar) <span class="keyword">if</span> low < sar: trend = -<span class="number">class="num">1</span> sar = ep ep = low af_current = af <span class="keyword">else</span>:
◍ 空头回补时 SAR 的翻转判定
上面这段逻辑处理的是价格在空头波段中触及 SAR 之后的状态切换。当 high 大于当前极值 ep 时,说明空方推动失效,系统先把 ep 刷新为 high,并把加速因子 af_current 按步长 af 累加,但封顶在 af_max,避免点数扩张过快。 若 high 没有越过 sar,则继续用 prev_sar + af_current * (ep - prev_sar) 推算本根 SAR;一旦后续 high 反过来站上 sar,趋势标回 1(多头),sar 直接锚定 ep,ep 重置为 high,af_current 回到初始 af。这种翻转在 EURUSD 15 分钟图上平均约每 40~60 根才触发一次,频率低但错位成本高。 若 high 始终没破 sar,则检查 low 是否低于 ep:低于就更新 ep 为更低低点,并同样累加加速因子(上限 af_max)。最后把算出的 sar 写回结果表的 SAR 列,函数返回整张 DataFrame 供绘图或告警。 开 MT5 把 af 从 0.02 调到 0.05、af_max 设 0.2,能明显看到翻转变密,但假信号也随之外汇贵金属的高风险被放大,验证时建议先用历史数据跑一遍再上模拟盘。
if high > ep: ep = high af_current = min(af_current + af, af_max) else: sar = prev_sar + af_current * (ep - prev_sar) if high > sar: trend = class="num">1 sar = ep ep = high af_current = af else: if low < ep: ep = low af_current = min(af_current + af, af_max) result_df.loc[i, &class="macro">#x27;SAR&class="macro">#x27;] = sar class="kw">return result_df