您应当知道的 MQL5 向导技术(第 64 部分):运用 DeMarker 和包络通道形态,搭配白噪内核·进阶篇
🐍

您应当知道的 MQL5 向导技术(第 64 部分):运用 DeMarker 和包络通道形态,搭配白噪内核·进阶篇

(2/3)· 当 ta 与 pandas 技术分析库各缺一角,从零实现指标反而更干净更快

含代码示例偏理论 第 2/3 篇
不少人在 Python 里跑 MQL5 指标时,默认装好库就能直接用,结果卡在 DeMarker 不在 pandas 库、包络线不在 ta 库。与其在不同库之间拼凑,自己写两个轻量函数反而少依赖、跑得稍快。

Demarker 撞包络下轨的看涨触发逻辑

特征-0 的核心是把 DeMarker 与价格包络线做联合判定:当 Demarker 主值 ≤0.3 且收盘价从包络下轨下方爬回上方时,才记为一列看涨信号;看跌则对称地要求 Demarker ≥0.7 且价格跌破上轨。 代码里先用 np.zeros((len(dem_df), 2)) 建一个与数据等长的二维零数组,第 0 列装看涨、第 1 列装看跌,未触发就是 0,避免索引错位。 看涨条件中 shift(1) 和 shift(2) 是关键:要求前一根收盘 ≤下轨、两根前收盘 ≥下轨,等于强制价格完成一次『触碰—离开』的下轨交汇。这种两周期确认比单根交叉多一层时态滤波,假信号概率倾向更低。 数组头两行直接置 0,因为移位操作在没有 t-1、t-2 数据时无法比较。返回的格式正好能喂进 RNN 输入层。外汇与贵金属波动大、滑点不可控,这类信号仅作概率参考,实盘前请在 MT5 用历史数据回测验证。

MQL5 / C++
def feature_0(dem_df, env_df, price_df):
    """
    """
    # Initialize empty array with class="num">2 dimensions and same length as input
    feature = np.zeros((len(dem_df), class="num">2))
    
    # Dimension class="num">1:
    feature[:, class="num">0] = ((dem_df[&class="macro">#x27;main&class="macro">#x27;] <= class="num">0.3) &
                            (price_df[&class="macro">#x27;close&class="macro">#x27;] > env_df[&class="macro">#x27;lower&class="macro">#x27;]) &
                            (price_df[&class="macro">#x27;close&class="macro">#x27;].shift(class="num">1) <= env_df[&class="macro">#x27;lower&class="macro">#x27;].shift(class="num">1)) &
                            (price_df[&class="macro">#x27;close&class="macro">#x27;].shift(class="num">2) >= env_df[&class="macro">#x27;lower&class="macro">#x27;].shift(class="num">2))).astype(class="type">int)
    feature[:, class="num">1] = ((dem_df[&class="macro">#x27;main&class="macro">#x27;] >= class="num">0.7) &
                            (price_df[&class="macro">#x27;close&class="macro">#x27;] < env_df[&class="macro">#x27;upper&class="macro">#x27;]) &
                            (price_df[&class="macro">#x27;close&class="macro">#x27;].shift(class="num">1) >= env_df[&class="macro">#x27;upper&class="macro">#x27;].shift(class="num">1)) &
                            (price_df[&class="macro">#x27;close&class="macro">#x27;].shift(class="num">2) <= env_df[&class="macro">#x27;upper&class="macro">#x27;].shift(class="num">2))).astype(class="type">int)
    
    # Set first class="num">2 rows to class="num">0 (no previous values to compare)
    feature[class="num">0, :] = class="num">0
    feature[class="num">1, :] = class="num">0
    
    class="kw">return feature

「包络线外的极值动能信号」

当 DeMarker 撞进超买(>0.7)或超卖(<0.3)极值,同时价格连续两个周期都挂在包络线上轨之外或下轨之外,这种组合往往暗示趋势还在跑而不是要回头。它和交叉类信号不同,不赌反转,只抓「已经出去且赖着不走」的延续状态。 代码里先铺一个全零二维数组,默认零值代表无信号,比留 NaN 更干净,也方便后面直接当整数掩码用。第 0 列算看涨:DeMarker 主值过 0.7、今收与昨收都高于上轨。第 1 列是镜像看跌:主值低于 0.3、今收与昨收都低于下轨。 第一行必须强制置零,因为移位比较需要前一周期数据,索引 0 没有「昨天」可供对齐。最终 return 的 NumPy 数组,0/1 分布可直接接进 MT5 的 Python 桥或本地回测框架里跑。 实盘里外汇与贵金属波动大、滑点凶,这种信号在欧美盘重叠时段触发概率更高,但仅提示「趋势延续倾向」,不等于进场就顺。建议先拿历史数据数一下:连续 2 根外轨 + DeMarker 极值的组合,在您常用周期(如 H1)出现后 3 根内同向收盘的比例,再决定阈值要不要放宽到 0.75 / 0.25。

MQL5 / C++
def feature_1(dem_df, env_df, price_df):
    """
    """
    # Initialize empty array with class="num">2 dimensions and same length as input
    feature = np.zeros((len(dem_df), class="num">2))
    
    # Dimension class="num">1:
    feature[:, class="num">0] = ((dem_df[&class="macro">#x27;main&class="macro">#x27;] > class="num">0.7) &
                            (price_df[&class="macro">#x27;close&class="macro">#x27;] > env_df[&class="macro">#x27;upper&class="macro">#x27;]) &
                            (price_df[&class="macro">#x27;close&class="macro">#x27;].shift(class="num">1) > env_df[&class="macro">#x27;upper&class="macro">#x27;].shift(class="num">1))).astype(class="type">int)
    feature[:, class="num">1] = ((dem_df[&class="macro">#x27;main&class="macro">#x27;] < class="num">0.3) &
                            (price_df[&class="macro">#x27;close&class="macro">#x27;] < env_df[&class="macro">#x27;lower&class="macro">#x27;]) &
                            (price_df[&class="macro">#x27;close&class="macro">#x27;].shift(class="num">1) < env_df[&class="macro">#x27;lower&class="macro">#x27;].shift(class="num">1))).astype(class="type">int)
    
    # Set first row to class="num">0 (no previous values to compare)
    feature[class="num">0, :] = class="num">0
    
    class="kw">return feature

◍ 用 DeMarker 极值配包络方向抓特征-5

特征-5 和前面两个特征同源,都是从 DeMarker 的极值里捞信号,但换了个视角:不看价格碰不碰包络波带,而是看包络带本身的方向。 具体规则很直白——DeMarker 主值冲过 0.7 且包络上轨比上一根抬高,记一个看涨;主值跌破 0.3 且下轨比上一根压低,记一个看跌。阈值 0.7 / 0.3 沿用了 DeMarker 通用的超买超卖分界。 代码里先建一个和输入等长的两列零矩阵,第一列装看涨、第二列装看跌。注意首行被强制置零,因为 shift(1) 在第一根没有前值,比较会出 NaN,不清掉会污染后续回测。 外汇和贵金属波动大、滑点凶,这套信号只代表概率倾向,实盘前务必在 MT5 用历史数据跑一遍前向漫游,确认在你用的品种周期上不退化。

MQL5 / C++
def feature_5(dem_df, env_df, price_df):
    """
    """
    # Initialize empty array with class="num">2 dimensions and same length as input
    feature = np.zeros((len(dem_df), class="num">2))
    
    # Dimension class="num">1:
    feature[:, class="num">0] = ((dem_df[&class="macro">#x27;main&class="macro">#x27;] > class="num">0.7) &
                        (env_df[&class="macro">#x27;upper&class="macro">#x27;] > env_df[&class="macro">#x27;upper&class="macro">#x27;].shift(class="num">1))).astype(class="type">int)
    feature[:, class="num">1] = ((dem_df[&class="macro">#x27;main&class="macro">#x27;] < class="num">0.3) &
                        (env_df[&class="macro">#x27;lower&class="macro">#x27;] < env_df[&class="macro">#x27;lower&class="macro">#x27;].shift(class="num">1))).astype(class="type">int)
    
    # Set first row to class="num">0 (no previous values to compare)
    feature[class="num">0, :] = class="num">0
    
    class="kw">return feature

用 DeMarker 与包络带捕捉波浪拐点

这套信号逻辑把动量变化和价格相对包络带的位置绑在一起:做多条件要求 DeMarker 主线向上抬、且最近四个周期的低点呈「下穿下轨—回到轨上—再下穿—再回轨上」的交替节奏;做空则反过来,DeMarker 向下、高点沿上轨走 M 型。 代码里 feature 是二维数组,列 0 记看涨、列 1 记看跌,满足即置 1。注意 shift(1) 到 shift(4) 的偏移覆盖了前四根 K 线,所以数组前 4 行若不做零值初始化,会因为 NaN 参与比较而污染后续信号。 外汇与贵金属杠杆高,这类形态只代表概率倾向,实盘前应在 MT5 用历史数据回放验证信号密度与假突破比例。

MQL5 / C++
def feature_6(dem_df, env_df, price_df):
    """
    """
    # Initialize empty array with class="num">2 dimensions and same length as input
    feature = np.zeros((len(dem_df), class="num">2))
    
    # Dimension class="num">1:
    feature[:, class="num">0] = ((dem_df[&class="macro">#x27;main&class="macro">#x27;] > dem_df[&class="macro">#x27;main&class="macro">#x27;].shift(class="num">1)) &
                            (price_df[&class="macro">#x27;low&class="macro">#x27;].shift(class="num">1) <= env_df[&class="macro">#x27;lower&class="macro">#x27;].shift(class="num">1)) &
                            (price_df[&class="macro">#x27;low&class="macro">#x27;].shift(class="num">2) >= env_df[&class="macro">#x27;lower&class="macro">#x27;].shift(class="num">2)) &
                            (price_df[&class="macro">#x27;low&class="macro">#x27;].shift(class="num">3) <= env_df[&class="macro">#x27;lower&class="macro">#x27;].shift(class="num">3)) &
                            (price_df[&class="macro">#x27;low&class="macro">#x27;].shift(class="num">4) >= env_df[&class="macro">#x27;lower&class="macro">#x27;].shift(class="num">4))).astype(class="type">int)
    feature[:, class="num">1] = ((dem_df[&class="macro">#x27;main&class="macro">#x27;] < dem_df[&class="macro">#x27;main&class="macro">#x27;].shift(class="num">1)) &
                            (price_df[&class="macro">#x27;high&class="macro">#x27;].shift(class="num">1) >= env_df[&class="macro">#x27;upper&class="macro">#x27;].shift(class="num">1)) &
                            (price_df[&class="macro">#x27;high&class="macro">#x27;].shift(class="num">2) <= env_df[&class="macro">#x27;upper&class="macro">#x27;].shift(class="num">2)) &
                            (price_df[&class="macro">#x27;high&class="macro">#x27;].shift(class="num">3) >= env_df[&class="macro">#x27;upper&class="macro">#x27;].shift(class="num">3)) &
                            (price_df[&class="macro">#x27;high&class="macro">#x27;].shift(class="num">4) <= env_df[&class="macro">#x27;upper&class="macro">#x27;].shift(class="num">4))).astype(class="type">int)
    
    # Set first class="num">4 rows to class="num">0 (no previous values to compare)
    feature[class="num">0, :] = class="num">0
    feature[class="num">1, :] = class="num">0
    feature[class="num">2, :] = class="num">0
    feature[class="num">3, :] = class="num">0
    
    class="kw">return feature

「DeMarker 滞后交叉抓动能反转」

这套特征把信号锚在 DeMarker 与包络波带的不同时间滞后上,核心不是预测方向,而是捕捉动能从极端区向中性转移的瞬间。做多侧的逻辑是:当前 DeMarker 已回到中性或看涨(≥0.5),但两周期前还在超卖(≤0.3),同时收盘价从包络上轨内侧切到外侧——这种滞后组合倾向确认超卖后的逆转或趋势启动。 看空侧对称处理:当前 DeMarker ≤0.5、两周期前 ≥0.8(超买),收盘价跌破包络下轨,且前一周期还在下轨之上。只用 shift(2) 引入两期滞后,是为了强制要求「近期确实出现过极端区」,过滤掉连续中性区的假突破。 下面这段 Python 实现可直接丢进你的特征工程管线,输入是 DeMarker 序列、包络上下轨和收盘价三张 DataFrame,输出两列 0/1 标记。 别把 0.3 和 0.8 当死参数 在黄金 1H 上回测,超卖阈值用 0.3 时信号频率明显低于 0.25,但假信号也少。外汇与贵金属波动结构不同,建议先在 MT5 导出 DeMarker 历史,按品种重估这两个边界。

MQL5 / C++
<span class="keyword">def</span> feature_7(dem_df, env_df, price_df):
&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"""
&nbsp;&nbsp;&nbsp;&nbsp;"""</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment"># Initialize empty array with class="num">2 dimensions and same length as input</span>
&nbsp;&nbsp;&nbsp;&nbsp;feature = np.zeros((<span class="built_in">len</span>(dem_df), <span class="number">class="num">2</span>))
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment"># Dimension class="num">1:DEM(X()) &gt;= class="num">0.5 &amp;&amp; DEM(X() + class="num">2) &lt;= class="num">0.3 &amp;&amp; Close(X()) &gt; ENV_UP(X()) &amp;&amp; Close(X() + class="num">1) &lt;= ENV_UP(X() + class="num">1)</span>
&nbsp;&nbsp;&nbsp;&nbsp;feature[:, <span class="number">class="num">0</span>] = ((dem_df[<span class="class="type">class="kw">string">&class="macro">#x27;main&class="macro">#x27;</span>] &gt;= <span class="number">class="num">0.5</span>) &amp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (dem_df[<span class="class="type">class="kw">string">&class="macro">#x27;main&class="macro">#x27;</span>].shift(<span class="number">class="num">2</span>) &lt;= <span class="number">class="num">0.3</span>) &amp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (price_df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>] &gt;= env_df[<span class="class="type">class="kw">string">&class="macro">#x27;upper&class="macro">#x27;</span>]) &amp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (price_df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>].shift(<span class="number">class="num">1</span>) &lt;= env_df[<span class="class="type">class="kw">string">&class="macro">#x27;upper&class="macro">#x27;</span>].shift(<span class="number">class="num">1</span>))).astype(<span class="built_in">class="type">int</span>)
&nbsp;&nbsp;&nbsp;&nbsp;feature[:, <span class="number">class="num">1</span>] = ((dem_df[<span class="class="type">class="kw">string">&class="macro">#x27;main&class="macro">#x27;</span>] &lt;= <span class="number">class="num">0.5</span>) &amp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (dem_df[<span class="class="type">class="kw">string">&class="macro">#x27;main&class="macro">#x27;</span>].shift(<span class="number">class="num">2</span>) &gt;= <span class="number">class="num">0.8</span>) &amp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (price_df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>] &lt;= env_df[<span class="class="type">class="kw">string">&class="macro">#x27;lower&class="macro">#x27;</span>]) &amp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (price_df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>].shift(<span class="number">class="num">1</span>) &gt;= env_df[<span class="class="type">class="kw">string">&class="macro">#x27;lower&class="macro">#x27;</span>].shift(<span class="number">class="num">1</span>))).astype(<span class="built_in">class="type">int</span>)
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment"># Set first row to class="num">0 (no previous values to compare)</span>
&nbsp;&nbsp;&nbsp;&nbsp;feature[<span class="number">class="num">0</span>, :] = <span class="number">class="num">0</span>
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">return</span> feature

◍ DeMarker 极值叠加包络线突破的稀有信号

最后一类形态盯着极端走势:当 DeMarker 闯进超买或超卖区,同时价格高低点明显戳出包络线波带,说明这根 K 线的动量已经脱离常态。具体看涨条件是 DeMarker 主值大于 0.7,且当期最低价高于包络线上轨——最低价都在上轨之上,意味着买盘把价格整体顶离了波动通道,上涨惯性可能较强。 看跌则是镜像:DeMarker 小于 0.3,且当期最高价低于包络线下轨。这两类场景在回测样本里出现频次很低,上一篇文章的测试中只触发了少量几笔交易,实盘里更难碰上。 因为信号本身稀有,直接跟单的滑点与假突破风险都不小,外汇和贵金属品种尤其容易被瞬时流动性打脸。建议接一层过滤,比如要求相邻两根 K 线都确认或结合成交量阈值,再让小布跑一遍验证命中率。

MQL5 / C++
def feature_8(dem_df, env_df, price_df):
    """
    """
    # Initialize empty array with class="num">2 dimensions and same length as input
    feature = np.zeros((len(dem_df), class="num">2))
    
    # Dimension class="num">1:DEM(X()) > class="num">0.7 && Low(X()) > ENV_UP(X())
    feature[:, class="num">0] = ((dem_df[&class="macro">#x27;main&class="macro">#x27;] > class="num">0.7) &
                        (price_df[&class="macro">#x27;low&class="macro">#x27;] > env_df[&class="macro">#x27;upper&class="macro">#x27;])).astype(class="type">int)
    feature[:, class="num">1] = ((dem_df[&class="macro">#x27;main&class="macro">#x27;] < class="num">0.3) &
                        (price_df[&class="macro">#x27;high&class="macro">#x27;] < env_df[&class="macro">#x27;lower&class="macro">#x27;])).astype(class="type">int)
    
    # Set first row to class="num">0 (no previous values to compare)
    # feature[class="num">0, :] = class="num">0
    
    class="kw">return feature
把指标对齐交给小布
这些 DeMarker 与包络通道的自定义信号诊断,小布盯盘的 AIGC 已内置,打开对应品种页即可看到跨周期对齐结果,你只需判断噪声过滤后的形态是否成立。

常见问题

该库未收录 DeMarker,它仅在 ta 模块中以原生形式提供;本文选择自行实现以避免混合依赖。
ta 提供了布林带与 Donchian 通道作为相关替代,但本文为统一实现仍自定义了包络线函数。
可以,小布盯盘的 AIGC 已内置类似诊断逻辑,对应品种页会呈现包络通道与 DeMarker 的协同视图,无需本地跑脚本。
DeMax 只保留最高价抬升部分、DeMin 只保留最低价降低部分,clip 将反向变动置零,从而分离上下行动能。
白噪内核用于压制价格序列中的随机扰动,使 DeMarker 与包络形态在机器学习训练时更少被无意义波动误导。