您应当知道的 MQL5 向导技术(第 64 部分):运用 DeMarker 和包络通道形态,搭配白噪内核·进阶篇
(2/3)· 当 ta 与 pandas 技术分析库各缺一角,从零实现指标反而更干净更快
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 用历史数据回测验证。
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。
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 用历史数据跑一遍前向漫游,确认在你用的品种周期上不退化。
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 用历史数据回放验证信号密度与假突破比例。
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 历史,按品种重估这两个边界。
<span class="keyword">def</span> feature_7(dem_df, env_df, price_df): <span class="class="type">class="kw">string">""" """</span> <span class="comment"># Initialize empty array with class="num">2 dimensions and same length as input</span> feature = np.zeros((<span class="built_in">len</span>(dem_df), <span class="number">class="num">2</span>)) <span class="comment"># Dimension class="num">1:DEM(X()) >= class="num">0.5 && DEM(X() + class="num">2) <= class="num">0.3 && Close(X()) > ENV_UP(X()) && Close(X() + class="num">1) <= ENV_UP(X() + class="num">1)</span> 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>] >= <span class="number">class="num">0.5</span>) & (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>) <= <span class="number">class="num">0.3</span>) & (price_df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>] >= env_df[<span class="class="type">class="kw">string">&class="macro">#x27;upper&class="macro">#x27;</span>]) & (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>) <= 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>) 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>] <= <span class="number">class="num">0.5</span>) & (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>) >= <span class="number">class="num">0.8</span>) & (price_df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>] <= env_df[<span class="class="type">class="kw">string">&class="macro">#x27;lower&class="macro">#x27;</span>]) & (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>) >= 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>) <span class="comment"># Set first row to class="num">0 (no previous values to compare)</span> feature[<span class="number">class="num">0</span>, :] = <span class="number">class="num">0</span> <span class="keyword">class="kw">return</span> feature
◍ DeMarker 极值叠加包络线突破的稀有信号
最后一类形态盯着极端走势:当 DeMarker 闯进超买或超卖区,同时价格高低点明显戳出包络线波带,说明这根 K 线的动量已经脱离常态。具体看涨条件是 DeMarker 主值大于 0.7,且当期最低价高于包络线上轨——最低价都在上轨之上,意味着买盘把价格整体顶离了波动通道,上涨惯性可能较强。 看跌则是镜像:DeMarker 小于 0.3,且当期最高价低于包络线下轨。这两类场景在回测样本里出现频次很低,上一篇文章的测试中只触发了少量几笔交易,实盘里更难碰上。 因为信号本身稀有,直接跟单的滑点与假突破风险都不小,外汇和贵金属品种尤其容易被瞬时流动性打脸。建议接一层过滤,比如要求相邻两根 K 线都确认或结合成交量阈值,再让小布跑一遍验证命中率。
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