您应当知道的 MQL5 向导技术(第 64 部分):运用 DeMarker 和包络通道形态,搭配白噪内核
概述
我们跟进一篇文章,基于 DeMarker 与支撑/阻力包络带动量指标配对,实证如何在机器学习中驾驭它们的信号。在近期文章中我们得到类似的指标配对方式,正在搜寻指导的读者能够参考这些。实质上,我们以 Python 语言实现 MQL5 指标,同时采用由 MetaTrader 5 Python 模块导入的价格数据。该模块允许您登录经纪商的服务器,提取价格数据和品种信息。

Python 中的指标
Python 拥有大量技术分析函数库,能够被轻松导入,并用于实现各种指标。不过当下的问题是,尽管它们并非全是标准的,当中还缺少一些指标。举例,本文中 pandas 技术分析库就缺少 DeMarker 振荡器,而该振荡器是在 “ta” 或技术分析模块中提供。另一方面,虽然包络线存在于 pandas 技术分析库之中,但在 “ta” 函数库中缺失了其原生形式。这是因为 “ta” 提供了相关的布林带和 Donchian 通道指标来替代。
有趣的是,这些天"从零开始"实现自己的指标,与安装这些函数库之一,并使用这些标准函数的忙乱差不多(甚至更省事)。故此,我们实现了自定义 DeMarker 和包络线函数,纸面上,这应当会令我们的 Python 在模块引用减少的情况下运行得略微快一些。
DeMarker
DeMarker 指标是一种振荡器,追踪特定周期资产买卖条件的极值。如上一篇文章所述,其范围在 0 到 1 之间,值高于 0.7 表示超买,低于 0.3 则表示超卖条件。我们选择如下以 Python 实现其自定义函数:
def DeMarker(df, period=14): """ Calculate DeMarker indicator for a DataFrame with OHLC prices Args: df: Pandas DataFrame with columns ['open', 'high', 'low', 'close'] period: Lookback period (default 14) Returns: Pandas Series with DeMarker values """ # Calculate DeMax and DeMin demax = df['high'].diff().clip(lower=0) demin = -df['low'].diff().clip(upper=0) # Smooth the values using SMA demax_sma = demax.rolling(window=period).mean() demin_sma = demin.rolling(window=period).mean() # Calculate DeMarker demarker = demax_sma / (demax_sma + demin_sma) return pd.DataFrame({'main': demarker})
我们函数的输入是一个数据帧 “df” 和 “period”。数据帧 “df” 是一个 pandas 数据帧,包含开盘价、最高价、最低价、和收盘价数据,而 period 是计算 DeMarker 的回溯周期。该函数封装了 DeMarker 逻辑,同时具备模块化、及复用性。
DeMax 的计算提供连续最高价的差值(df[‘high’].diff())。在该缓冲区中,如果差值为正数值,这意味着高点上升,且在缓冲区内保留该数值,否则该数值设为零。设置为零的操作由 clip 函数(clip(lower=0))执行。类似地,对于最低价差值,如果差值为负数值(即当前最低价低于前低点),该值作为绝对值保留在缓冲区之中,否则也由 clip(clip(upper=0))将其设为零。
跟踪这些绝对变化很重要,因为 DeMax 通过记录最高价抬升来衡量价格上行动能,而 DeMin 通过记录最低价降低来衡量价格下行动能。这两个量值是 DeMarker 的核心,因为它们根据价格相对于前期的走位多少来设定。在 Python 中,利用 diff() 函数实现时,实质上是确保输入数据帧有足够的数据点,并且注意第一行始终有 NaN 值。
之后,我们覆盖每个缓冲区中的数值进行平滑处理。平滑化降低了每个序列的噪声,令指标值对短期价格波动的敏感度降低。rolling(window=period)..mean() 方法计算覆盖指定周期的平均值,这引入了一个滞后,往往与指标识别宏观趋势的目的相符。由于滚动窗口数据不足,起始 period-1 数值将为 NaN,且它们应通过舍弃、或以归一化值填充来处理。周期选择也影响 DeMarker 的灵敏度,周期越短,灵敏度越高。
然后我们计算 DeMarker 值,即 DeMax 均值除以 DeMin 均值,加上 DeMin 均值。这一步将 DeMarker 归一化为 0 到 1 的范围,简化了解释。该比率有效衡量价格上涨幅度相对于价格上下浮动总额的强度。接近 1 的数值表示强劲的看涨动能,接近 0 的数值则表示看跌动能更强。
当实现时,关键是确保该步骤不出现除零。不过这罕有发生,因为价格总是在变化,尤其在最高价和最低价。函数随后返回 DeMarker 值,其为单一列的 Pandas 数据帧,我们选择将其标记为 “main”。数据帧格式确保了与其它基于 panda 的分析工具的兼容性,同时便于整合到更大的交易系统之中。
包络线
价格包络线是一个支撑/阻力指标,围绕价格的移动平均线绘制了两条波带,分别是上轨带和下轨带。这些波带按固定 deviation 百分比偏置,这有助于交易者在价格触及这些波带时,获得支撑和阻力区域的位置。我们的 Python 版本实现如下:
def Envelopes(df, period=14, deviation=0.1): """ Calculate Price Envelopes for a DataFrame with OHLC prices Args: df: Pandas DataFrame with columns ['open', 'high', 'low', 'close'] period: MA period (default 20) deviation: Percentage deviation from MA (default 0.05 for 5%) Returns: DataFrame with columns ['upper', 'ma', 'lower'] """ # Calculate moving average (typically using close prices) ma = df['close'].rolling(window=period).mean() # Calculate upper and lower envelopes upper = ma * (1 + deviation) lower = ma * (1 - deviation) return pd.DataFrame({'upper': upper, 'ma': ma, 'lower': lower})
上面我们的代码提供了一种灵活的方式来计算包络线,并可针对不同策略定制 periods 和 deviation 输入参数。计算包络线的第一步是计算移动平均线。我们使用覆盖输入周期收盘价的简单移动平均。移动平均线作为包络线的中心线,也是趋势的平均价格。rolling(window=period).mean() 缓冲区计算返回均值的向量/缓冲区,但第一个 period-1 的数值如预期为 NaN。这些都应当得到相应处理。
然后我们计算上轨带和下轨带缓冲区数值。包络上轨的计算是将 MA 乘以(1 + deviation)。例如,如果 deviation 是 10%,那么上轨带将是 MA 的 110%。由于我们的默认 deviation 值为 0.1,这相当于 10%。包络下轨的计算是将 MA 乘以(1 - deviation),在我们的情况下是 90%。
上下轨定义了在“正常”条件下价格预计波动的价格范围。当价格触及任一波带时,会出现突破、或回归的场景,因为我们将这些波带视为支撑和阻力位。这两种状况都指出趋势延续、或行情的超买/超卖振荡。
deviation 参数控制包络线的宽度。deviation 百分比越高,意味着波带越宽,适合波动性资产;deviation 越低,对于稳定资产创建的波带越紧贴。deviation 应为正数值,以避免无效波带。这些包絡波带围绕移动平均线对称,假设波动在趋势上、下方均等。对于非对称波带,可以采用布林带。
返回的 pandas 数据帧包含 “upper”、“lower”、和 “ma” 三列。该数据帧格式允许轻松访问这三个分量,能够可视化、信号生成、或进一步分析。
Python 的特征
我们所指特征是来自 DeMarker 和包络线两个指标的信号的矢量化表示。这些特征随后作为我们机器学习模型的输入,其为一个循环神经网络,使用白噪内核,稍后会有更多详述。这个机器学习以 Python 实现,是建立在我们上一篇文章中初次考察的共 10 个特征。这 10 个当中只有 6 个能够前向漫游。0、1、5、6、7 和 8。因此,我们将通过循环神经网络(RNN)测试这些特征。就此,我们必须在投喂给 RNN 之前,先以 Python 手写代码。
特征-0
正如我们在上一篇文章中所见,特征-0 基于 DeMarker 穿叉超买或超卖阈值、且价格同时穿越包络线的上轨或下轨来生成信号。因此,我们如下实现 Python 版本
def feature_0(dem_df, env_df, price_df): """ """ # Initialize empty array with 2 dimensions and same length as input feature = np.zeros((len(dem_df), 2)) # Dimension 1: feature[:, 0] = ((dem_df['main'] <= 0.3) & (price_df['close'] > env_df['lower']) & (price_df['close'].shift(1) <= env_df['lower'].shift(1)) & (price_df['close'].shift(2) >= env_df['lower'].shift(2))).astype(int) feature[:, 1] = ((dem_df['main'] >= 0.7) & (price_df['close'] < env_df['upper']) & (price_df['close'].shift(1) >= env_df['upper'].shift(1)) & (price_df['close'].shift(2) <= env_df['upper'].shift(2))).astype(int) # Set first 2 rows to 0 (no previous values to compare) feature[0, :] = 0 feature[1, :] = 0 return feature
在函数中,我们的第一行代码创建了一个填充了零指的 NumPy 数组,塑形(len(dem_df), 2),分类别存储看涨和看跌信号。它初始化数组,确保所有行默认为零值。数组大小必须与数据帧长度相匹配,以避免索引错位。
看涨检查索引在 0 位,且先被检查。正如上一篇文章中论调,来自我们以上的实现,当 DeMarker 处于超卖地域(通常低于 0.3)时,会产生看涨信号;当前收盘价高于包络下轨;前收盘价处于、或低于包络下轨;而两周期前的收盘价则位于、或高于包络线下轨。
看涨检查条件结合了 DeMarker 超卖条件、与价格突破包络线下轨,暗示潜在的趋势延续或逆转。shift(1) 和 shift(2) 条件确保价格已与上一篇文章定义的包络线下轨发生了交汇。实际上,能够增加一些额外的测量。这些可能包括多周期检查,通过要求特定价格形态来降低假信号。此外,针对 env_df['lower'] 和 price_df['close'] 验证 NaN 值,会避免无效比较。
看跌列标识当 DeMarker 处于超买地域时产生的信号;当前收盘价低于包络线上轨;前收盘价位于、或高于包络线上轨;而两周期收盘价处于、或低于包络线上轨。该形态捕捉超买条件后潜在出现的逆转或回调。当使用时,应确保足够历史数据,以便处理移位操作。
为此,我们为初始行的前两个数值设为零。这是因为我们使用了 shift(1) 和 shift(2) 的数值。
return 语句返回包含信号形态的 NumPy 数组。这为我们 RNN 的输入提供了合适的格式。多周期价格包络的交汇(shift(1) 和 shift(2))增加了时态确认层,实际上是一个额外的滤波器,当与其它简单交叉信号相比时,其更具唯一性。
特征-1
当 DeMarker 处于超买或超卖极值区域,且价格连续数周期保留在包络的上、下轨之外时,这种形态就会创建信号。我们如下实现 Python 版本:
def feature_1(dem_df, env_df, price_df): """ """ # Initialize empty array with 2 dimensions and same length as input feature = np.zeros((len(dem_df), 2)) # Dimension 1: feature[:, 0] = ((dem_df['main'] > 0.7) & (price_df['close'] > env_df['upper']) & (price_df['close'].shift(1) > env_df['upper'].shift(1))).astype(int) feature[:, 1] = ((dem_df['main'] < 0.3) & (price_df['close'] < env_df['lower']) & (price_df['close'].shift(1) < env_df['lower'].shift(1))).astype(int) # Set first row to 0 (no previous values to compare) feature[0, :] = 0 return feature
我们的第一步,正如特征-0,是初始化数组大小,并填充零值。按默认初始化意味着没有信号,这比 NaN 要好。接下来我们定义每个索引处的数值。对于第一个指数/列,我们检查看涨条件。我们的代码检查 DeMarker 是否处于超买地域(>0.7);当前收盘价位于包络线上轨之上;且前收盘价也高于包络线上轨。
这很重要,因为它识别出强劲看涨动能形势,尽管 DeMarker 处于超买,价格仍能维持高于包络线上轨。正如上一篇文章的论调,它表明趋势延续,胜过逆转。据此,我们的代码会往前设置下一个索引值,用于检查看跌条件。清单检查 DeMarker 是否处于超卖地域(<0.3);当前收盘价低于包络线斯阿鬼;而前一收盘价也低于包络线下轨。
看多信号则是所预期镜像,它捕捉强烈的看跌动能,而价格走势持续低于包络线下轨。这是下跌趋势延续的标记,正如上一篇文章中的论调。鉴于我们正用移位比较,我们需要将第一行的值设为 0。我们仅设置了第一行,因为比较移位仅针对一个索引。return 语句生成 NumPy 格式的信号数组。
该函数捕捉超出包络线波带之外的持续价格突破,并由 DeMarker 极值确认,是趋势持续性强劲的迹象。它主要是一个参与现有趋势的机会来临的信号,而非预测逆转。它与特征-0 不同,因为它专注于波带之外的连续数个周期,且无需交叉形态。
特征-5
下一个形态是特征-5,在前一篇文章中能够通过前向漫游测试。与我们上面提到的两个特哼有点类似,它从 DeMarker 的极值中获得信号,不过它使用包络波带的方向,取代专注于价格与波带之间的交汇。我们如下实现 Python 版本:
def feature_5(dem_df, env_df, price_df): """ """ # Initialize empty array with 2 dimensions and same length as input feature = np.zeros((len(dem_df), 2)) # Dimension 1: feature[:, 0] = ((dem_df['main'] > 0.7) & (env_df['upper'] > env_df['upper'].shift(1))).astype(int) feature[:, 1] = ((dem_df['main'] < 0.3) & (env_df['lower'] < env_df['lower'].shift(1))).astype(int) # Set first row to 0 (no previous values to compare) feature[0, :] = 0 return feature
第一笔生意,如其它两个函数,是将输出数组初始化为零,并将其大小设置为 2。上述我们的函数中,这在第一行代码中得到了满足。然后我们设置第一个索引值,如其它特征一样,检查看涨。此处需求相对简单,如果 DeMarker 超买(>0.7),且包络线上轨抬高,则我们得到一个看涨信号。接下来,我们设置第二个索引数值来检查看跌。正如预期,DeMarker 低于 0.3,包络线下轨下降,指向看跌信号。然后,如预期,我们将第一行设为零值,以避免在移位比较时,因 NaN 的无效比较。
特征-6
如上一篇文章中讲述的特征-6 或形态-6,其在生成信号时,基于 DeMarker 动量变化、及在包络指标波带上形成的价格波浪。我们如下实现 Python 版本:
def feature_6(dem_df, env_df, price_df): """ """ # Initialize empty array with 2 dimensions and same length as input feature = np.zeros((len(dem_df), 2)) # Dimension 1: feature[:, 0] = ((dem_df['main'] > dem_df['main'].shift(1)) & (price_df['low'].shift(1) <= env_df['lower'].shift(1)) & (price_df['low'].shift(2) >= env_df['lower'].shift(2)) & (price_df['low'].shift(3) <= env_df['lower'].shift(3)) & (price_df['low'].shift(4) >= env_df['lower'].shift(4))).astype(int) feature[:, 1] = ((dem_df['main'] < dem_df['main'].shift(1)) & (price_df['high'].shift(1) >= env_df['upper'].shift(1)) & (price_df['high'].shift(2) <= env_df['upper'].shift(2)) & (price_df['high'].shift(3) >= env_df['upper'].shift(3)) & (price_df['high'].shift(4) <= env_df['upper'].shift(4))).astype(int) # Set first 4 rows to 0 (no previous values to compare) feature[0, :] = 0 feature[1, :] = 0 feature[2, :] = 0 feature[3, :] = 0 return feature
初始化协议类似于我们上面所考察的,但如预期,在指定数组值的形式上有所不同。对于检查看涨的第一个索引,如果满足做多条件,我们赋值 1(等价于 true)。该条件为 DeMarker 递增,且伴随收盘价相对于包络线下轨,高于前四个周期高低交替蜡烛。第二个索引检查看跌,搜寻 DeMarker 是否正在下降,就像 M 型收盘价形态在包络线上轨的看涨确认。
接下来把初始行设置为零值,执行这一步是避免无效的指标比较。如果不赋以零值,则会产生无效比较,因为默认值是 NaN,而 NaN 源于移位比较。因此,我们从 0 到 3 赋零值,跨度为 4 行,因为我们所用的索引偏移从 1 到 4。
特征-7
正如最后一篇文章中论调,这一特征基于 DeMarker 值在不同时间滞后、及包络波带与价格交叉处生成信号。这也带来了专注动能转移。我们如下实现 Python 版本:
def feature_7(dem_df, env_df, price_df): """ """ # Initialize empty array with 2 dimensions and same length as input feature = np.zeros((len(dem_df), 2)) # Dimension 1:DEM(X()) >= 0.5 && DEM(X() + 2) <= 0.3 && Close(X()) > ENV_UP(X()) && Close(X() + 1) <= ENV_UP(X() + 1) feature[:, 0] = ((dem_df['main'] >= 0.5) & (dem_df['main'].shift(2) <= 0.3) & (price_df['close'] >= env_df['upper']) & (price_df['close'].shift(1) <= env_df['upper'].shift(1))).astype(int) feature[:, 1] = ((dem_df['main'] <= 0.5) & (dem_df['main'].shift(2) >= 0.8) & (price_df['close'] <= env_df['lower']) & (price_df['close'].shift(1) >= env_df['lower'].shift(1))).astype(int) # Set first row to 0 (no previous values to compare) feature[0, :] = 0 return feature
初始化大小为 2,并填充零值,第一个索引用于检查做多信号。我们的代码标记看涨为真的条件,如果当前 DeMarker 是中性或看涨(>=0.5);两周期前的 DeMarker 超卖(<=0.3);当前收盘价位于或低于包络线上轨。这些长度需求旨在捕捉从超卖至中性或看涨的动能转变,并由突破包络线上轨之上作为确认。这通常表明出现了强烈的逆转、或趋势启动。shift(2) 条件引入滞后,要求近期是超卖条件。
在索引 1 处检查看跌条件,如果满足当前 DeMarker 处于中性至看跌(<= 0.5);两周期前的 DeMarker 超买(>=0.8);当前收盘价位于或低于包络线下轨;最后,前一个收盘价位于或高于包络线下轨。我们仅将第一行赋为零值,因为我们仅用一个位移索引。
特征-8
我们的最后一个特征,是在 DeMarker 处于极值区间,且价格低点/高点明显超出包络线波带时获得信号,意味着极端价格走势。我们如下实现 Python 版本:
def feature_8(dem_df, env_df, price_df): """ """ # Initialize empty array with 2 dimensions and same length as input feature = np.zeros((len(dem_df), 2)) # Dimension 1:DEM(X()) > 0.7 && Low(X()) > ENV_UP(X()) feature[:, 0] = ((dem_df['main'] > 0.7) & (price_df['low'] > env_df['upper'])).astype(int) feature[:, 1] = ((dem_df['main'] < 0.3) & (price_df['high'] < env_df['lower'])).astype(int) # Set first row to 0 (no previous values to compare) # feature[0, :] = 0 return feature
先以零值初始化输出 NumPy 数组,并将其大小设为 2,然后设置索引 0 处数值。该索引正如上述所有形态一样,是其看涨与否的标记。在这种情况下,看涨信号是 DeMarker 超买(>0.7);当前最低价位于包络线上柜之上。该形态典型示意强劲的看涨走势,因为该周期的最低价格超过包络线上轨。这通常意味着有显著的上涨势头。
在索引 1 处检查看跌,如果 DeMarker 超卖(<0.3),当前高点低于包络线下轨,则得到确认信号。上述看涨设定的镜像,这两种场景都很罕见,不过自我们在上一篇文章中的测试,我们能碰到少量交易。但在实盘状况下,鉴于稀有性,出于安全性,可额外附加确认过滤器。
Python 版本 RNN
我们的循环神经网络(RNN)接收以上来自特征 0、1、5、6、7 和 8 的矢量化指标输出,是一个 PyTorch 神经网络模块,结合了标准 RNN,与噪声注入机制,从而增强健壮性、或模型随机过程。它专为回归任务设计,每个输入序列产生单一输出值。这种噪声注入应用于 RNN 的隐藏状态,由投影层和 sigmoid 激活调制,以控制其影响。
该 RNN 的输入是一个塑形张量(batch_size, input_size)。该设计有一个 RNN 层、后随一个线性投影层处理噪声,最后一个全连接层输出。执行噪声注入,得益于处理 RNN 隐态的白噪声内核。这能够实时完成,亦可作为预先计算的张量提供。输出是对应每个序列的单一回归值,其塑形为(batch_size,)。 Python 版本实现如下:
# Define the network as a class class WhiteNoiseRNN(nn.Module): def __init__(self, input_size=5, hidden_size=64, num_layers=1): super().__init__() self.hidden_size = hidden_size self.rnn = nn.RNN(input_size, hidden_size, num_layers, batch_first=True) self.noise_proj = nn.Linear(hidden_size, hidden_size) # Projects noise to hidden space self.fc = nn.Linear(hidden_size, 1) # Single output for regression def forward(self, x, noise_std=0.05): """ Args: x: Input tensor of shape (batch_size, seq_len, input_size) noise_std: Either: - float: Standard deviation for generated noise - tensor: Pre-computed noise (must match rnn_out shape: batch_size × seq_len × hidden_size) """ # Ensure proper input dimensions if x.dim() == 2: x = x.unsqueeze(1) # (batch, features) → (batch, 1, features) # RNN processing rnn_out, _ = self.rnn(x) # (batch_size, seq_len, hidden_size) # Noise injection if isinstance(noise_std, torch.Tensor): noise_std = noise_std.float() if noise_std.dim() == 1: noise_std = noise_std.view(-1, 1, 1) # (batch,) → (batch, 1, 1) noise = noise_std.expand_as(rnn_out) elif noise_std > 0: noise = torch.randn_like(rnn_out) * noise_std else: noise = 0 if isinstance(noise, torch.Tensor): projected_noise = self.noise_proj(noise) rnn_out = rnn_out + torch.sigmoid(rnn_out) * projected_noise return self.fc(rnn_out[:, -1, :]) # (batch_size,)
从我们上面的清单中,WhiteNoiseRNN 类被定义为 nn.Module 的一个子类,且其初始化依据三个输入参数:输入大小、隐藏层大小、和层数。这一步确立了网络架构和超参数。
继承自 nn.Module,PyTorch 的自动升级和模型管理功能,譬如训练和评估,都可轻松使用。输入大小的选择取决于输入数据的特征维度。在我们的情况下,所有特征都有 2-维,因此该数值会是 2。
隐藏尺寸设置模型的容量。更大的数值提升模型的表现力,但风险是过度拟合、以及计算量提升。层数设为 1 足以应对大多数简单任务或问题,不过提升该数值会导致梯度消失问题。
我们调用 super() 初始化来调用父类,nn.Module,并确保正确设置 PyTorch 模块功能,譬如参数跟踪和设备管理。包含该调用作为一条规则,通常能避免许多初始化错误。隐藏大小参数作为实例变量存储,供其它潜在方法所用。
我们按指定输入大小、隐藏大小、和层数,初始化 RNN 类变量,作为一个 RNN 层。将参数 “batch-first” 赋值为 true,确保取所处理批次作为塑形输入和输出张量的大小,作为塑形内的第一个维度。然后我们定义了一个噪声投影层,将噪声投射到与 RNN 隐态相同的维度之中。这令噪声可被变换,无论是通过缩放、亦或旋转、等等,在被加入隐藏状态之前,如此这般噪声注入便是可学习的。这就提升了模型在训练中适应噪声影响的能力。线性层添加了参数,提升了模型的复杂度,故此需要足够的训练。
然后我们定义一个全连通层,将 RNN 的最终隐藏状态映射到单一输出值。这会产生回归输出,将隐藏态的维数降低到单一标量。这对模型任务至关重要,尤其是在需要预测连续值的情况下,就如同我们这种状况。
前向方法定义了网络的前向通验,处理输入 x,并按标准差 noise_std 应用噪声。该函数实现核心计算、组合、RNN 处理、噪声注入、并输出预测。确保 x 在输入前始终经过正确塑形、和预处理至关重要。默认 noise_std 为 0.05 是一个超参数,需要根据所需的噪声等级进行调谐。如果使用预先计算的噪声,其塑形和类型应加以验证,以避免运行时错误。
前向函数的第一个动作是通过添加一个长度维数为 1 的序列来重塑 x。这确保了与 RNN 的兼容性,即使在单时间步输入时,也期望一个序列维度。它还令模型具备灵活性,可应对单步和多步输入两者。
接下来我们按输入 x 处理 RNN,生成每个时间步的隐藏状态。该输出有两个方面。首先我们取 rnn_out,这是一个包含隐藏状态的张量;其次我们得到 _,这是最终层的隐藏状态,但在此情况下我们忽略它,因为我们没有用到它。这种处理至关重要,因为 RNN 捕捉输入序列中的时态依赖性,形成模型顺序处理的骨干。这些隐藏状态是噪声注入和输出预测的主要机制。
之后,我们继续处理预先计算的噪声,正如顶部的 if-从句所检查。我们将张量转换为浮点,以保证一致性,重塑一维张量以便广播,并将噪声扩展,从而匹配 rnn_out 的塑形。备案则是,如果产生噪声,则我们为隐藏状态引入随机性,令其在建模随机过程中更具健壮性。噪声通过 noise_std 进行伸缩,控制其强度。否则,如果没有噪声,且 noise_std 为零或负数值,则噪声也被赋予零值。这允许模型无噪声运行,这对判定性预测、或当调试时颇具建设性。
赋予噪声值后,我们经由 noise_proj 线性层投射噪声,令其与隐藏状态空间一致。使用 RNN 输出的 sigmoid 来调制噪声的影响,并将其加入隐藏状态。如前所述,这种线性投影令噪声注入可学习,允许模型在训练期间调整噪声的影响。torch.sigmoid(rnn_out) 项将噪声在 0 到 1 之间伸缩,确保它不会压倒隐藏状态。有论调,该方式通过引入受控随机函数来增加健壮性,从而预防过度拟合。
然后我们选择最后一个时间步的隐藏状态,并将其传递到全连接层,每个序列产生单一输出。最后一个时间步的隐藏状态汇总了序列中的信息,这适用于回归任务。fc 层将隐藏状态映射到所需的输出。
测试运行
按照我们的网络定义,我们如上一篇文章中执行六个形态的前向漫游测试;分别是 0、1、5、6、7 和 8。在上一篇文章之前,我们尝试过较长的网络输入,因为它们并未遵照看涨或看跌状态把更多指标信号配对。
本文中不曾这样做,因为所有形态都明显有看涨检查索引,和看跌检查索引。这或许能解释为什么上一篇文章的前向漫游测试百分比更高,而之前文章则没有做到这一点。我们以 GBPUSD 进行测试。训练运行依据 2023 年 H4 时间帧使用 Python 执行。Python 训练为我们给出做多、或者做空的指导。
由于在 MetaTrader 5 中导入的 ONNX 模型随后会由一个向导汇编的智能系统所用,其使用加权做多和做空条件,这些数值也需要优化,这些也依据 2023 年度完成。
训练和优化后期,我们依据 2023.01.01 到 2025.01.01 数据执行测试运行,以下显示所有 6 个形态/特征的结果:


特征-0 走不通!


特征-1 走不通!


特征-5 走通


特征-6 走不通!


特征-7 走通


特征-8 走通
结束语
我们已实证了结合 DeMarker 和包络线指标所产生的形态。作为动量振荡器和支撑/阻力指标,在上一篇文章中它们首次进行互补配对测试,其中交易基础无修正形态。在本文中,我们经由一个循环神经网络来处理这些形态,其在训练中运用了白噪内核来处理相同的指示形态。我们还研究一种能将所有形态整合在一起的神经网络,有些是依据无修正信号形态人为虚构的。我们会在即将发表的文章中考察这一点。
| 名称 | 描述 |
|---|---|
| wz64.mq5 | 向导汇编的智能系统,其头文件显示用到的文件。 |
| SignalWZ_64.mqh | 自定义信号类文件 |
| 64_0.onnx | 导出的 ONNX 模型用于特征-0 |
| 64_1.onnx | 导出了的 特征-1 的 ONNX 模型 |
| 64_5.onnx | 针对特征-5 |
| 64_6.onnx | 针对特征-6 |
| 64_7.onnx | 针对特征-7 |
| 64_8.onnx | 针对特征-8 |
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/18033
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
从基础到中级:事件(二)
新手在交易中的10个基本错误
MQL5交易工具(第四部分):为多周期扫描仪表盘添加动态定位与切换功能