English Русский Deutsch 日本語
preview
您应当知道的 MQL5 向导技术(第 61 部分):结合 ADX 和 CCI 形态进行监督学习

您应当知道的 MQL5 向导技术(第 61 部分):结合 ADX 和 CCI 形态进行监督学习

MetaTrader 5交易系统 |
23 0
Stephen Njuki
Stephen Njuki

概述

我们继续考察如何把跟踪市场不同层面的指标与机器学习配对,来构建一个交易系统。至于接下来的文章,我们会考察平均方向性指数(ADX)振荡器与商品通道指数(CCI)的配对。ADX 主要作为一个趋势确认指标,而 CCI 则是一个动量指标。当我们在之前的文章中考察单个指标形态时,曾接触过这两个属性。回顾一下,趋势确认衡量的是给定价格趋势如何强劲;而强度指的是适合入场。动量指标则从另一方面衡量价格变化率。价格在给定方向上变化越快,遭遇不利波动的可能性就越小。

图标



商品通道指数(CCI)

我们正在测试我们的网络,并利用 Python 取指标信号作为输入。这是因为目前 Python 相较于 MQL5 提供了显著的性能优势。然而,正如我在最近的文章中提到的,MQL5 通过使用 OpenCL,能够达到、甚至接近这样的性能。不过,这需要配备一块 GPU,才能享受 OpenCL 的速度。至于目前,当两种语言在相似的 CPU 上使用时,Python 显然出类拔萃,故我们据其测试和开发我们的模型。

MetaTrader 已构建了一个 Python 模块,不仅允许将经纪商价格数据加载到 Python,还允许自 Python 下单交易。更多相关内容可以在这里这里找到。因此,我们利用部分模块登录经纪商,并将价格数据拉入 Python,用于我们的监督学习 MLP。一旦我们得到价格数据,随后我们就要定义指标函数。如果我们从 CCI 开始,我们可如下实现:

def CCI(df, period=14):
    """
    Calculate Commodity Channel Index (CCI)
    :param df: pandas DataFrame with columns ['high', 'low', 'close']
    :param period: lookback period (default 20)
    :return: Series with CCI values
    """
    # Calculate Typical Price
    typical_price = (df['high'] + df['low'] + df['close']) / 3
    
    # Calculate Simple Moving Average of Typical Price
    sma = typical_price.rolling(window=period).mean()
    
    # Calculate Mean Deviation
    mean_deviation = typical_price.rolling(window=period).apply(
        lambda x: np.mean(np.abs(x - np.mean(x))), raw=True)
    
    # Calculate CCI
    df['cci'] = (typical_price - sma) / (0.015 * mean_deviation)
    
    return df[['cci']]

CCI 是一个振荡指标,即衡量资产价格偏离统计平均值的程度。这对交易者识别超买或超卖条件非常有帮助。我们上述实现的函数采用 pandas 数据帧,包含“最高价”、“最低价”、和“收盘价”列,并有一个计算 CCI 值的回望周期(默认为 14)。“典型价格”计算设定为最高价、最低价、和收盘价的平均值,代表/覆盖平均周期大部分的价格动作。它是这三个主要价格在每个时间帧下的算术均值,有助于把价格数据简化为一个代表性数值。这可辅助降低日内波动带来的噪音。这一步是基础,因为 CCI 基于距这一典型价格的偏离,同时用这三个(最高价、最低价、收盘价)帮助确保价格走势的平衡。因此,需要确保通过 MetaTrader 5 模块检索经纪商价格数据的 Pandas 数据帧包含所有这些列,因为缺了这些数值会导致错误。

简单移动平均线(SMA)平滑了指定周期内的典型价格,建立一条基线。这是计算指定输入周期(默认 14)的 SMA,如此它就能像一个参考来行事。这很重要,因为短期价格波动会被平滑,从而提供一个具有代表性的“正常”价格水平。滚动函数需要足够的数据点,至少是输入周期的平方,才能计算一条有效的 SMA。如果数据集太短,那么初始 NaN 值或许需要特殊处理(经由像是 dropna() 函数,等等)

平均偏差随后衡量这些代表性典型价格与其 SMA 绝对偏差的平均值,从而捕捉价格波动性。对于每个窗口,计算每个典型价格与窗口均值之间的绝对差值,然后取其平均值。这是根本,因为平均偏差会放大价格波动性,而波动性对于 CCI 的伸缩至关重要,而 CCI 又深刻反映资产典型价格摇摆。这也确保了不同资产之间的比较。lambda 函数针对大数据集计算密集,这就是为何使用向量化替代、或像 ta-lib 这样的函数库是个好主意。在进行中,仍然需要导入 NumPy。

CCI 公式随后将上述所有分量合并,计算出一个数值,然后按常数 0.015 进行缩放,以实现归一化。0.015 常数是一个标准的缩放因子,旨在保持 CCI 值在 ±100 范围。不过这并非总是能达成,不过这就是目标。这是 CCI 公式的核心,因为它将原始价格偏差转化为标准化的振荡器。高于 +100 的数值则表示超买,低于 -100 则表示超卖。按该公式,如果平均偏差为零,应当注意除零错误。虽然罕见,但价格横盘时这种场景是可能的。然后根据需要,可在分母中加入一个小的 ε 值(例如 1e-10),以缓解这一点。 

return 语句返回一个仅包含 “cci” 列的数据帧。如果调试或额外分析需要额外的列(例如 “typical_price”),则可修改该语句,包含所需列。



平均方向性指数(ADX)

如上所述,ADX 衡量趋势强度,无论方向如何。这是通过使用主要由两个缓冲区(+DI 和 -DI)组成的方向走势索引做到的。该函数与上述 CCI 类似,按“最高价”、“最低价”、“收盘价” 列的 pandas 数据帧、及回望周期(默认 14)来计算 ADX、+DI 和 -DI 的数值。其 Python 的实现如下:

def ADX(df, period=14):
    """
    Calculate Average Directional Index (ADX)
    :param df: pandas DataFrame with columns ['high', 'low', 'close']
    :param period: lookback period (default 14)
    :return: DataFrame with ADX, +DI, -DI columns
    """
    # Calculate +DM, -DM, and True Range
    df['up_move'] = df['high'] - df['high'].shift(1)
    df['down_move'] = df['low'].shift(1) - df['low']
    df['+dm'] = np.where(
        (df['up_move'] > df['down_move']) & (df['up_move'] > 0),
        df['up_move'],
        0.0
    )
    df['-dm'] = np.where(
        (df['down_move'] > df['up_move']) & (df['down_move'] > 0),
        df['down_move'],
        0.0
    )
    
    # Calculate True Range
    df['tr'] = np.maximum(
        df['high'] - df['low'],
        np.maximum(
            abs(df['high'] - df['close'].shift(1)),
            abs(df['low'] - df['close'].shift(1))
    ))
    
    # Smooth the values using Wilder's smoothing method (EMA with alpha=1/period)
    df['+dm_smoothed'] = df['+dm'].ewm(alpha=1/period, adjust=False).mean()
    df['-dm_smoothed'] = df['-dm'].ewm(alpha=1/period, adjust=False).mean()
    df['tr_smoothed'] = df['tr'].ewm(alpha=1/period, adjust=False).mean()
    
    # Calculate +DI and -DI
    df['+di'] = 100 * (df['+dm_smoothed'] / df['tr_smoothed'])
    df['-di'] = 100 * (df['-dm_smoothed'] / df['tr_smoothed'])
    
    # Calculate DX
    df['dx'] = 100 * (abs(df['+di'] - df['-di']) / (df['+di'] + df['-di']))
    
    # Calculate ADX
    df['adx'] = df['dx'].ewm(alpha=1/period, adjust=False).mean()
    
    # Return the relevant columns
    return df[['adx', '+di', '-di']]

两个计算方向性走势的缓冲区(+DM, -DM),放入价格向上和向下走势的大小,以供评估方向性强度。它们是从连续最高价走势(上升移动)与相反方向(连续最低点之差,下降移动)之间的差值推导而来。这很重要,因为这些构成了+DM 和 -DM 模块,进而设定方向性动量。通过使用平移值,我们确保跨周期比较。当 +DM 超过向下移动、且为正数值时,假定其为上升移动;否则,+DM 为 0。类似地,当 -DM 超过向上移动,也给其一个向下移动的数值,令其变为正数值。这有助于过滤掉非主导、或负值走势。

NumPy 的 where() 函数对于向量化运算非常有效,因此您应该确保 NumPy 被导入。验证上行和下行的计算正确性也很重要,以避免误判走势。使用 shift(1) 在第一行引入了 NaN,这在下游计算、或返回结果时需要处理。从始至终的根本,要确保“最高价”和“最低价”列是数字。

真实范围 TR 衡量价格波动率,有助于参考缺口和日内范围。取三个值中最高的一个来执行计算;最高价-最低价区间、绝对最高价至先前收盘价的范围,以及绝对最低价至先前收盘价的范围。这有助于参考价格缺口和波动性。测量资产波动率非常重要,因为它会当作归一化 +DI 和 -DI 的分母。这意味着 ADX 体现了相对于价格走势的趋势强度。NumPy 的 maximum 函数确保选取最大范围。自 shift(1) 处理 NaN 也应被关注。

怀尔德(Wilder)平滑法针对 +DM、-DM 和 TR,应用了特殊 alpha 的指数移动平均。alpha 是 1/period,adjust 参数的 False 意味着更多权重会赋予近期数据,这模仿了怀尔德的原创方法。平滑有助于降低噪声,令指标数据集在趋势分析中更加可靠。ewm 函数高效,但对 alpha 参数敏感。 

方向性指标依据真实范围归一化已平滑走势,以便比较多头与空头的强度。100 的比例有助于用百分比来表达它们。这很重要,因为 +DI 和 -DI 以数字标示多头和空头趋势相对于波动率的程度。因此,+DI 和 -DI 之间的交叉处是潜在趋势变化的信号。增加一个小的 ε 值,就能起到除零保护作用。100 的缩放因子是标准的,是为了轻松解读。

DX 缓冲区判定 +DI 与 -DI 之间的伸缩绝对差值,再除以它们的合计。这是方向性走势的相对强度大小的根基。DX 是 ADX 的重要中间步骤,任务是捕捉趋势的“烈度”(无论是多头亦或空头)。应当小心处理 +di + -di 为零的情况,避免除零错误。可以返回零,或者跳过计算。

最后,ADX 取方向性指数平均值,来估算整体趋势强度。DX 值的平滑,若采用怀尔德 EMA 计算 ADX,倾向于反映覆盖长久运行的趋势强度。这是最终的指标输出,高于 25 的数值标记强劲趋势,低于 20 的则暗示市场疲软或范围振荡。出于连贯性,重点要确保跨所有平滑步骤的 alpha 值一致。

return 语句生成一个包含 “adx”、“+di” 和 “-di” 列的数据帧,其为 ADX 指标的完整数据缓冲区集合。这为交易者提供了趋势分析所需的相关量值,附加了 “dx” 或 “tr” 中间列,能用来调试或定制指标。



特征

我们将这两个函数的读数并到一起,创造一个多维信号数组,本质上结合了 ADX(针对趋势强度)和 CCI(针对动量),识别特定市场条件,譬如趋势初启、逆转、或超买/超卖状态。自这两个指标合并后产生的信号,就是我们所说的更广泛层面上的“特征”。回想一下,在前四篇文章中,当我们研究 MA 和随机振荡器的指标配对时,我们得到 5 个主要数据集。这些分别是特征、状态、动作、奖励、和编码。此处的这些特征与我们当时得到的等同。 

这两个指标的配对所采用的前提,就是自它们的配对生成的 10 个特征形态。当然,数量可能更多,但就我们的目的,这就足够了。我们为每个形态分配了一个函数。每个函数会返回一个 NumPy 数组,每列代表一个条件(维度),其中 1 表示条件已满足,0 表示不满足。这些函数将取 pandas 数据帧作为输入。这些输入被标记为 adx_df(“adx”、“+di”、“-di” 列);cci_df(“cci” 列);可选 price_df(“high”、“low”、“close” 列)。

我们利用 Python 实现这些函数,以便加快测试流程,但也需要实现类似的 MQL5 版本,从而可部署/运用在我们的最终智能系统。MQL5 内置的 CCI 和 ADX 中已具备指标点处理,意味当智能系统用到它们时,这些都不是问题。回顾一下,对于 Python 来说,应当验证 adx_df 和 cci_df,以便确保所需的列存在,NaN 值也需一同处理,或舍弃、或填充它值,以避免比较错误。移位操作如 shift(1) 会固有在第一行引入 NaN。因此,将第一行设置为 0 是管理这一点的标准方法。理想情况下,对于大数据集应当引入矢量化运算,因为所用的 NumPy where() 函数和 astype(int) 或许不够给力。

所有特征都是依据 EURUSD 的 2020 年 1 月 1 日至 2024 年 1 月 1 日的日线时间帧数据进行测试/训练。前向漫游测试区间为 2024.01.01 至 2025.01.01。只有特征-2、3 和 4 能够通过前向漫游测试,故此它们的结果会在各自的描述中分享。



特征-0

该形态基于 ADX > 25 且 CCI 在 ±100 交叉。它为突破动量、或趋势开端提供确认。25 水平对 ADX 具有重要意义,因此每当它与其交叉,且 CCI 与关键水平 100 交叉时,新趋势来临的概率很高。这两个指标的运用有助于过滤噪声。在寻找趋势时,等待 ADX 测试 25 水平始终很重要。任何低于 20 的都应避开。在以趋势闻名的资产,这是一种高概率的设定,比如像一些股票指数,等等。

自这些信号生成的特征向量是一个 6-维向量。我们的 Python 和 MQL5 实现如下:

def feature_0(adx_df, cci_df):
    """
    Creates a 3D signal array with the following dimensions:
    1. ADX > 25 crossover (1 when crosses above 25, else 0)
    2. CCI > +100 crossover (1 when crosses above +100, else 0)
    3. CCI < -100 crossover (1 when crosses below -100, else 0)
    """
    # Initialize empty array with 3 dimensions and same length as input
    feature = np.zeros((len(adx_df), 6))
    
    # Dimension 1: ADX > 25 crossover
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    feature[:, 1] = (adx_df['adx'].shift(1) <= 25).astype(int)
    
    # Dimension 2: CCI > +100 crossover
    feature[:, 2] = (cci_df['cci'] > 100).astype(int)
    feature[:, 3] = (cci_df['cci'].shift(1) <= 100).astype(int)
    
    # Dimension 3: CCI < -100 crossover
    feature[:, 4] = (cci_df['cci'] < -100).astype(int)
    feature[:, 5] = (cci_df['cci'].shift(1) >= -100).astype(int)
    
    # Set first row to 0 (no previous values to compare)
    feature[0, :] = 0
    
    return feature
   if(Index == 0)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] > 25 ? 1.0f : 0.0f);
         _features[1] = (_adx[1] <= 25 ? 1.0f : 0.0f);
         _features[2] = (_cci[0] > 100 ? 1.0f : 0.0f);
         _features[3] = (_cci[1] <= 100 ? 1.0f : 0.0f);
         _features[4] = (_cci[0] < -100 ? 1.0f : 0.0f);
         _features[5] = (_cci[1] >= -100 ? 1.0f : 0.0f);
      }
   }

我们已将该形态的 MLP 输入向量分解为其关键成分信号。这些是:ADX 之前低于 25,随后 ADX 高于 25,CCI 之前低于 +100,CCI 现在高于 +100,CCI 之前高于 -100,CCI 目前低于 -100。在这种设定下,显然不会所有状况能同时为真。它允许我们自定义所有价格数据点,替代围绕典型的形态逻辑来将它们分组。

传统上,看涨设定是当 ADX 从 25 下方穿过,并收于 25 上方,随后 CCI 也从 +100 下方穿过,并收于 +100 上方。同样,看跌形态是 ADX 再次穿过 25 关卡,并收于上方,但 CCI 从上方穿过 -100 关卡,并收于其下方。如果我们生成严格的向量,即仅测试这些“真实”的看涨或看跌设定,那么我们的输入向量将是大小为 3,且在庞大的测试数据中变异更小。我们选择的 6-维输入数据选项捕捉这些传统量值,也有“连续”数据,否则这些数据会被更多“离散/传统”设置所忽略。



特征-1

该形态围绕 ADX > 25 且 CCI 从对立面穿过 ±50 关卡。它是在已建立趋势中的再次入场动量。鉴于 ADX 确认趋势完整性,CCI 检测短期逆势后的复原,这对于回调后持续交易非常理想。这种形态适合顺势交易者,他们看重回踩后顺势入场。CCI 在穿过零轴也是一个重要提示,不应操之过急。对于顺势交易者,若正在寻求保护收益,可在该信号处设置尾随停止。我们的 Python 和 MQL5 实现如下:

def feature_1(adx_df, cci_df):
    """
    Creates a modified 3D signal array with:
    1. ADX > 25 (1 when above 25, else 0)
    2. CCI crosses from below 0 to above +50 (1 when condition met, else 0)
    3. CCI crosses from above 0 to below -50 (1 when condition met, else 0)
    """
    # Initialize empty array with 3 dimensions
    feature = np.zeros((len(adx_df), 5))
    
    # Dimension 1: ADX above 25 (continuous, not just crossover)
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    
    # Dimension 2: CCI crosses from <0 to >+50
    feature[:, 1] = (cci_df['cci'] > 50).astype(int)
    feature[:, 2] = (cci_df['cci'].shift(1) < 0).astype(int)
    
    # Dimension 3: CCI crosses from >0 to <-50
    feature[:, 3] = (cci_df['cci'] < -50).astype(int)
    feature[:, 4] = (cci_df['cci'].shift(1) > 0).astype(int)
    
    # Set first row to 0 (no previous values to compare)
    feature[0, :] = 0
    
    return feature
   else if(Index == 1)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] > 25 ? 1.0f : 0.0f);
         _features[1] = (_cci[0] > 50 ? 1.0f : 0.0f);
         _features[2] = (_cci[1] < 0 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] < -50 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] > 0 ? 1.0f : 0.0f);
      }
   }

我们的函数生成一个 5-维二进制输出向量,其成分为:ADX 是否高于 25 关卡,之前的 CCI 是否高于 0;当前 CCI 是否低于或等于 -50;之前的 CCI 是否低于 0;当前的 CCI 是否高于或等于 50。传统上,看涨设定是 ADX 高于 25,而 CCI 之前低于零,但现在高于 50。看跌设定是,如果 ADX 再次高于 25,而 CCI 之前高于 0,但现在低于或等于 -50。上述有关行情更多朝着延续/回归移动,而非断断续续的观点,也适用于此。



特征-2

该形态基于 ADX > 25,且价格与 CCI 提供了背离。这也是一种背离设定、或趋势内的逆转。它采用经典的动量背离形态,并以 ADX 作为趋势滤波器。这表明即使趋势仍然有效,也潜在逆转可能。该形态适合与价格动作、或支撑/阻力结合的状况,以便更好地确认。它的理想状况,是在长时间移动后形成背离。不过建议谨慎,背离往往只是早期信号,因为若正处于强劲趋势,它们当中许多会误判。我们的 Python 和 MQL5 实现如下:

def feature_2(adx_df, cci_df, price_df):
    """
    Creates a 5D signal array with:
    1. ADX > 25 (1 when above 25, else 0)
    2. Lower low (1 when current low < previous low, else 0)
    3. Higher CCI (1 when current CCI > previous CCI, else 0)
    4. Higher high (1 when current high > previous high, else 0)
    5. Lower CCI (1 when current CCI < previous CCI, else 0)
    """
    # Initialize empty array with 5 dimensions
    feature = np.zeros((len(price_df), 5))
    
    # Dimension 1: ADX above 25
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    
    # Dimension 2: Lower low
    feature[:, 1] = (price_df['low'] < price_df['low'].shift(1)).astype(int)
    
    # Dimension 3: Higher CCI
    feature[:, 2] = (cci_df['cci'] > cci_df['cci'].shift(1)).astype(int)
    
    # Dimension 4: Higher high
    feature[:, 3] = (price_df['high'] > price_df['high'].shift(1)).astype(int)
    
    # Dimension 5: Lower CCI
    feature[:, 4] = (cci_df['cci'] < cci_df['cci'].shift(1)).astype(int)
    
    # Set first row to 0 (no previous values to compare)
    feature[0, :] = 0
    
    return feature
   else if(Index == 2)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2 && CopyRates(Symbol(),Period(),T, 2, _r) >= 2)
      {  _features[0] = (_adx[0] > 25 ? 1.0f : 0.0f);
         _features[1] = (_r[0].low <= _r[1].low ? 1.0f : 0.0f);
         _features[2] = (_cci[0] > _cci[1] ? 1.0f : 0.0f);
         _features[3] = (_r[0].high > _r[1].high ? 1.0f : 0.0f);
         _features[4] = (_cci[0] < _cci[1] ? 1.0f : 0.0f);
      }
   }

我们的函数输出是一个 6-维向量,包括:ADX 是否大于 25;价格低点下移更低;CCI 上移更高;价格高点上移更高;最后,CCI 是否下移更低。按说,看涨设定的组成是由 ADX 在 25 上方、在动量上升时录得更低的最低价,如 CCI 标记。类似地,看跌设定中 ADX 仍会在 25 以上,但价格将创下更高的最高价,而 CCI 却录得下落。

r2

c2



特征-3

该形态结合 ADX 上升,及中立区的 CCI。它以 CCI 处于中立区,权当趋势确认。它专注于上升或下降趋势中的持续动量。当 CCI 处于中性区(0 到 +/-100)时,它往往意味着价格维稳,不会极端移动。它倾向于比超买/超卖信号更安全,假入场风险更低。在波动较小的市场中,这可取之作为“趋势是您的朋友”。结合移动平均线排列、或价格结构,它能够提供更高的安全性。我们的 Python 和 MQL5 实现如下:

def feature_3(adx_df, cci_df):
    """
    Creates a 3D signal array with:
    1. ADX rising (1 when current ADX > previous ADX, else 0)
    2. CCI between 0 and +100 (1 when in range, else 0)
    3. CCI between 0 and -100 (1 when in range, else 0)
    """
    # Initialize empty array with 3 dimensions
    feature = np.zeros((len(adx_df), 5))
    
    # Dimension 1: ADX rising
    feature[:, 0] = (adx_df['adx'] > adx_df['adx'].shift(1)).astype(int)
    
    # Dimension 2: CCI between 0 and +100
    feature[:, 1] = (cci_df['cci'] > 0).astype(int)
    feature[:, 2] = (cci_df['cci'] < 100).astype(int)
    
    # Dimension 3: CCI between 0 and -100
    feature[:, 3] = (cci_df['cci'] < 0).astype(int)
    feature[:, 4] = (cci_df['cci'] > -100).astype(int)
    
    # Set first row to 0 (no previous ADX value to compare)
    feature[0, :] = 0
    
    return feature
   else if(Index == 3)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] > _adx[1] ? 1.0f : 0.0f);
         _features[1] = (_cci[0] > 0 ? 1.0f : 0.0f);
         _features[2] = (_cci[1] < 100 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] < 0 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] > -100 ? 1.0f : 0.0f);
      }
   }

我们的函数生成一个 5-维向量,其组成:ADX 是否增加;CCI 是否大于 0;CCI 是否低于 +100;CCI 是否低于 0;以及 CCI 是否高于 -100。传统的看涨设定是上升的 ADX,CCI 在 0 以上,但低于 +100。翻折看跌形态同样是 ADX 上升,但 CCI 低于 0,但高于 -100。该形态强调 ADX 的上升(不仅是 > 25)。此外,中性 CCI 范围倾向于针对早期趋势发展阶段,这与特征-0 的极端交叉示例不同。

r3



特征-4

该形态采用的设定是 ADX > 25,且 CCI 从极端处复原。这是一种失败摇摆设定。它捕捉到动量陷阱,其中 CCI 突破了极限关卡,却未能继续。ADX 的加入确保了这不是盘整内的拉锯状况。该形态往往在逆转、或急剧回弹之前见到。它最好用在波动交易时段(或新闻驱动事件,像是如非农就业报告发布)。此处关键是要观察失败的摇摆日的灯芯蜡烛(尖刺柱线),获得强烈的确认。我们的 Python 和 MQL5 实现如下:

def feature_4(adx_df, cci_df):
    """
    Creates a 3D signal array with:
    1. ADX > 25 (1 when above 25, else 0)
    2. CCI dips below -100 then closes above it (1 when condition met, else 0)
    3. CCI breaks above +100 then closes below it (1 when condition met, else 0)
    """
    feature = np.zeros((len(cci_df), 5))
    
    # Dimension 1: ADX above 25
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    
    # Dimension 2: CCI dips below -100 then closes above it
    feature[:, 1] = (cci_df['cci'].shift(1) < -100).astype(int)
    feature[:, 2] = (cci_df['cci'] > -100).astype(int)
    
    # Dimension 3: CCI breaks above +100 then closes below it
    feature[:, 3] = (cci_df['cci'].shift(1) > 100).astype(int)
    feature[:, 4] = (cci_df['cci'] < 100).astype(int)
    
    return feature
   else if(Index == 4)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] > 25 ? 1.0f : 0.0f);
         _features[1] = (_cci[0] < -100 ? 1.0f : 0.0f);
         _features[2] = (_cci[1] > -100 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] > 100 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] < 100 ? 1.0f : 0.0f);
      }
   }

我们的特征-4 函数为我们给出一个 5-维向量,输出 1 和 0 二进制值:ADX 低于 20;CCI 低于 -100;CCI 现高于 -100;CCI 高于 +100;CCI 现低于 +100;典型的看涨信号要搜寻动量转移,因此若 ADX 低于 20,且 CCI 从 -100 下方移至 -100 上方之时。因此,如果 ADX 再次跌破 20,是盛行趋势疲弱迹象,而 CCI 从 +100 上方收于 +100 下方,表示正面动量下落,也是翻折看跌形态。

r4

c4



特征-5

该形态简单地采用 ADX < 20,且 CCI 极端激增。它作为低波动性即将动量激增的前兆。它在 ADX 低迷期间观察 CCI 的爆发,有助于发现突破的早期阶段。不仅如此,它还意图帮助交易者在趋势开始前建仓。当实现该形态时,采用紧凑的停止挡往往是个好主意,因为很多尖峰可能是假动作。当该形态与其它指标结合时,譬如布林带挤压、或成交量突破,可能带来额外优势。不过,它更适合较短的时间帧(例如 M15 到 H1),因为这些对于快速动量交易更便利。我们的 Python 和 MQL5 实现如下:

def feature_5(adx_df, cci_df):
    """
    Creates a 3D signal array with:
    1. ADX < 20 (1 when below 20, else 0) - indicates weak trend
    2. CCI spikes above 150 (1 when condition met, else 0) - extreme overbought
    3. CCI drops below -150 (1 when condition met, else 0) - extreme oversold
    """
    # Initialize array
    feature = np.zeros((len(cci_df), 5))
    
    # Dimension 1: ADX below 20 (weak trend)
    feature[:, 0] = (adx_df['adx'] < 20).astype(int)
    
    # Dimension 2: CCI spikes above 150 (sudden extreme overbought)
    # Using 2-bar momentum to detect "sudden" spikes
    feature[:, 1] = (cci_df['cci'] > 150).astype(int)
    feature[:, 2] = (cci_df['cci'].shift(1) < 130).astype(int)
    
    # Dimension 3: CCI drops below -150 (sudden extreme oversold)
    # Using 2-bar momentum to detect "sudden" drops
    feature[:, 3] = (cci_df['cci'] < -150).astype(int)
    feature[:, 4] = (cci_df['cci'].shift(1) > -130).astype(int)
    
    return feature
   else if(Index == 5)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] < 20? 1.0f : 0.0f);
         _features[1] = (_cci[0] > 150 ? 1.0f : 0.0f);
         _features[2] = (_cci[1] < 130 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] < -150 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] > -130 ? 1.0f : 0.0f);
      }
   }

我们的特征-5 函数生成一个 5-维向量,投喂给我们的 MLP。捕获的信号是:ADX 是否低于 20;CCI 是否高于 +150;此前 CCI 是否低于 +130;CCI 是否低于 -150;最后 CCI 是否高于 -130。它用 ADX 在 25 以上的标记来确保强劲趋势正在上演,旨在检测 CCI 从极端关卡的复原,并按惯例专注于逆转、或动量转移。

典型的看涨设定是当 ADX 高于 25 时,CCI 曾低于 +130,且现在处于 +150。类似地,看跌设定也要求 ADX 必须高于 25,CCI 现处于 -150,之前测试处于 -130。



特征-6

该特征是关于 ADX 下穿 40 以下,且 CCI 与 ±100 交叉。针对趋势耗竭的离场信号,该形态以 ADX 下跌为标记,是趋势疲软的迹象。一旦 CCI 也回到中立或对立侧,这是动量衰败的预兆。它可被视为风险降低信号,作为调整尾随停止、或止盈的标记。它也能与烛条形态结合,从而干净利索地离场,不过,按该设定入场开立新交易往往不明智。我们的 Python 和 MQL5 实现如下:

def feature_6(adx_df, cci_df):
    """
    Creates a 3D signal array with:
    1. ADX crosses below 40 (1 when crosses down, else 0)
    2. CCI crosses below +100 (1 when crosses down, else 0)
    3. CCI crosses above -100 (1 when crosses up, else 0)
    """
    # Initialize array with zeros
    feature = np.zeros((len(cci_df), 6))
    
    # Dimension 1: ADX crosses below 40
    feature[:, 0] = (adx_df['adx'] < 40).astype(int)
    feature[:, 1] = (adx_df['adx'].shift(1) >= 40).astype(int)
    
    # Dimension 2: CCI crosses below +100
    feature[:, 2] = (cci_df['cci'] < 100).astype(int)
    feature[:, 3] = (cci_df['cci'].shift(1) >= 100).astype(int)
    
    # Dimension 3: CCI crosses above -100
    feature[:, 4] = (cci_df['cci'] > -100).astype(int)
    feature[:, 5] = (cci_df['cci'].shift(1) <= -100).astype(int)
    
    return feature
   else if(Index == 6)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] < 40? 1.0f : 0.0f);
         _features[1] = (_adx[1] < 40? 1.0f : 0.0f);
         _features[2] = (_cci[0] < 100 ? 1.0f : 0.0f);
         _features[3] = (_cci[1] >= 100 ? 1.0f : 0.0f);
         _features[4] = (_cci[0] > -100 ? 1.0f : 0.0f);
         _features[5] = (_cci[1] <= -100 ? 1.0f : 0.0f);
      }
   } 

我们的特征-6 函数输出一个 6-维向量,包括:ADX 现是否低于 40;ADX 是否此前高于 40;CCI 是否低于 100;CCI 是否此前高于 100;CCI 是否高于 -100;最后,CCI 之前是否低于 -100。看跌终结形态(等同于看涨)是指 ADX 从 40 跌至 40 下方,而 CCI 曾低于 -100,但现在已高于该关卡。相较之,看涨终结形态(等同于看跌)是指 ADX 再次从 40 下跌,CCI 也从 +100 上方跌破该关卡。它最适合作为离场、或退出警告形态,不过于此包含它,是出于测试目的,当作入场信号。



特征-7

这个涉及 ADX > 25,且 CCI 与零线交叉。一旦 CCI 与零线交叉,它就是一个趋势捕捉器。这是因为 CCI 穿越零线,权当作为动量枢轴点。由于 ADX 也在确认强度,这一设定提供了干净的趋势中段入场。当价格节节走高、或节节走低时,该形态更可作为依靠。据该形态作为信号,在多个点入场可能值得研究。应当针对时间对齐、或会话波动窗口完成回测。我们的 Python 和 MQL5 实现如下:

def feature_7(adx_df, cci_df):
    """
    Creates Feature-7 3D signal array with:
    1. ADX > 25 (1 when above 25, else 0) - trend strength
    2. CCI crosses above 0 (1 when bullish crossover, else 0)
    3. CCI crosses below 0 (1 when bearish crossover, else 0)
    """
    # Initialize array with zeros
    feature = np.zeros((len(cci_df), 5))
    
    # Dimension 1: ADX above 25 (continuous signal)
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    
    # Dimension 2: CCI crosses above 0 (bullish)
    feature[:, 1] = (cci_df['cci'] > 0).astype(int)
    feature[:, 2] = (cci_df['cci'].shift(1) <= 0).astype(int)
    
    # Dimension 3: CCI crosses below 0 (bearish)
    feature[:, 3] = (cci_df['cci'] < 0).astype(int)
    feature[:, 4] = (cci_df['cci'].shift(1) >= 0).astype(int)
    
    return feature
   else if(Index == 7)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] > 25? 1.0f : 0.0f);
         _features[1] = (_cci[0] > 0? 1.0f : 0.0f);
         _features[2] = (_cci[1] <= 0 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] < 0 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] >= 0 ? 1.0f : 0.0f);
      }
   } 

我们的特征-7 函数也返回一个 5-维输出向量。该向量记录:ADX 是否高于 25;CCI 是否高于 0;CCI 之前是否低于或等于 0;CCI 是否低于 0;最后,CCI 之前是否高于或等于 0。其典型形态是,如果 ADX 高于 25;且 CCI 曾低于 0,但现在高于 0,则暗示看涨信号。类似地,看跌形态是 ADX 再次高于 25,而 CCI 此前高于 0,现低于 0。


特征-8

我们的第九个特征信号依赖 ADX < 20,且 CCI 自极值回调。它相当于 ADX 强度过滤器,加上 CCI 超买/超卖逆转指标。这是经典的方位逆转,经 ADX 过滤。在疲软趋势或拉锯行情中,CCI 逆转倾向表现更好。理想情况下,它应仅在 ADX 真的很低(低于 20)时使用,且不应在趋势行情中应用。与布林带或 RSI 配对,适合多指标确认。这种逆转形态对于均值回归资产可能很理想,像是大宗商品、或货币对。我们的 Python 和 MQL5 实现如下:

def feature_8(adx_df, cci_df):
    """
    Creates a 3D signal array with:
    1. ADX < 20 (1 when below 20, else 0) - weak trend
    2. CCI rises from -200 to -100 (1 when condition met, else 0) - extreme oversold bounce
    3. CCI falls from +200 to +100 (1 when condition met, else 0) - extreme overbought pullback
    """
    # Initialize array with zeros
    feature = np.zeros((len(cci_df), 5))
    
    # Dimension 1: ADX below 20 (weak trend)
    feature[:, 0] = (adx_df['adx'] < 20).astype(int)
    
    # Dimension 2: CCI rises from -200 to -100
    # Using 2-bar lookback to detect the move
    feature[:, 1] = (cci_df['cci'] > -100).astype(int)
    feature[:, 2] = (cci_df['cci'].shift(1) <= -200).astype(int)
    
    # Dimension 3: CCI falls from +200 to +100
    # Using 2-bar lookback to detect the move
    feature[:, 3] = (cci_df['cci'] < 100).astype(int)
    feature[:, 4] = (cci_df['cci'].shift(1) >= 200).astype(int)
    
    return feature
   else if(Index == 8)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 2)
      {  _features[0] = (_adx[0] < 20? 1.0f : 0.0f);
         _features[1] = (_cci[0] > -100? 1.0f : 0.0f);
         _features[2] = (_cci[1] <= -200 ? 1.0f : 0.0f);
         _features[3] = (_cci[0] < 100 ? 1.0f : 0.0f);
         _features[4] = (_cci[1] >= 200 ? 1.0f : 0.0f);
      }
   }

特征-8 的函数输出也是 5-维二进制值向量。这些,标记:ADX 是否低于 20;CCI 超过 -100;CCI 低于 -200;CCI 低于 +100;高于 +200。因此,来自这些形态的看涨信号是在 ADX 低于 20,且 CCI 此前测试 -200 后上穿 -100 时。相较之,看跌形态是 ADX 再次跌破 20,CCI 此前在 +200 以上,后跌破 +100。



特征-9

我们的最后一个特征再次采用 ADX > 25,且 CCI 延迟交叉。该形态代表对立信号抑制、或陷阱滤波器。它擅长发现与盛行趋势对立的假突破、或假动作。在这些设定下,价格往往暗示逆转,不过 CCI 通过重申盛行趋势来拒绝这一观点。它适合防范趋势陷阱。它可结合烛条确认、或假动作后成交量下降。那些因虚假先验信号而被“灼伤“、需要一款置信过滤器的交易者来说,这很不错。我们的 Python 和 MQL5 实现如下:

def feature_9(adx_df, cci_df):
    feature = np.zeros((len(cci_df), 7))
    cci = cci_df['cci'].values
    
    # Dimension 1
    feature[:, 0] = (adx_df['adx'] > 25).astype(int)
    
    # Dimension 2: Below 0 then above +50 within 20 periods
    feature[:, 1] = (cci < 0).astype(int)
    feature[:, 2] = (np.roll(cci, 1) >= 0).astype(int)
    below_zero = (feature[:, 1]==1) & (feature[:, 2]==1)
    feature[:, 3] = 0
    for i in np.where(below_zero)[0]:
        if i+20 < len(cci) and np.max(cci[i+1:i+21]) > 50:
            feature[:, 3] = 1
            break
    
    # Dimension 3: Above 0 then below -50 within 20 periods
    feature[:, 4] = (cci > 0).astype(int)
    feature[:, 5] = (np.roll(cci, 1) <= 0).astype(int)
    feature[:, 6] = 0
    above_zero = (feature[:, 4]==1) & (feature[:, 5]==1)
    for i in np.where(above_zero)[0]:
        if i+20 < len(cci) and np.min(cci[i+1:i+21]) < -50:
            feature[:, 6] = 1
            break
    
    return feature
   else if(Index == 9)
   {  if(CopyBuffer(A.Handle(), 0, T, 2, _adx) >= 2 && CopyBuffer(C.Handle(), 0, T, 1, _cci) >= 21)
      {  _features[0] = (_adx[0] > 25? 1.0f : 0.0f);
         _features[1] = (_cci[0] < 0? 1.0f : 0.0f);
         _features[2] = (_cci[1] >= 0 ? 1.0f : 0.0f);
         _features[3] = (_cci[ArrayMaximum(_cci,1,20)] > 50 ? 1.0f : 0.0f);
         _features[4] = (_cci[0] > 0? 1.0f : 0.0f);
         _features[5] = (_cci[1] <= 0 ? 1.0f : 0.0f);
         _features[6] = (_cci[ArrayMinimum(_cci,1,20)] < -50 ? 1.0f : 0.0f);
      }
   } 

该形态生成一个 7-维向量,映射为:ADX 大于 25;CCI 是否低于 0;之前的 CCI 是否高于 0;之前 20 根柱线至最后一根柱线期间,CCI 是否高于 50 关卡;CCI 是否高于 0;之前的 CCI 是否低于 0;最后,在之前 20 根柱线至最后一根柱线期间,CCI 是否跌破了 -50。

来自这些形态生成的指示信号如下。为了获得看涨信号,ADX 需要高于 25,且 CCI 需要在跌破 0 之前曾尝试高于 0,且在下跌前 20 根柱内最高达至 50。同样,看跌形态是指 CCI 刚跌破 0,之前曾在 0上方,且在 0 上方前的 20 根柱线内至少曾低于 -50。该形态的测试结果未进行前向漫游测试,故此就不分享了,但所有源码都附在底部,供后续独立测试。



结束语

我们在监督学习的 MLP 中研究了 ADX 和 CCI 的联动形态,前向漫游测试结果优劣参杂。这是一次尝试,输入向量更连续、更低离散,正如我们在有关移动平均和随机振荡器的第 57 篇文章中所做的那样。尽管这或许是罪魁祸首,我们在接下来的文章中仍会坚持该方式,在于我们也要见识其它机器学习方法如何配合这些指标运作。

名称 描述
61_0.onnx 针对形态-0 的 MLP:
61_1.onnx 针对形态-1 的 MLP:
61_2.onnx 针对形态-2 的 MLP:
61_3.onnx 针对形态-3 的 MLP:
61_4.onnx 针对形态-4 的 MLP:
61_5.onnx 针对形态-5 的 MLP:
61_6.onnx 针对形态-6 的 MLP:
61_7.0nnx 针对形态-7 的 MLP:
61_8.onnx 针对形态-8 的 MLP:
61_9.onnx 针对形态-9 的 MLP:
61_x.mqh 形态处理文件
SignalWZ_61.mqh 信号类文件
wz_61.mq5 向导汇编的智能系统,展示包含文件

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/17910

附加的文件 |
61_0.onnx (265.8 KB)
61_1.onnx (264.8 KB)
61_2.onnx (264.8 KB)
61_3.onnx (264.8 KB)
61_4.onnx (264.8 KB)
61_5.onnx (264.8 KB)
61_6.onnx (265.8 KB)
61_7.onnx (264.8 KB)
61_8.onnx (264.8 KB)
61_9.onnx (266.8 KB)
61_X.mqh (5.67 KB)
SignalWZ_61.mqh (15.85 KB)
wz_61.mq5 (8.06 KB)
在 MQL5 中实现其他语言的实用模块(第 02 部分):构建受 Python 启发的 REQUESTS 库 在 MQL5 中实现其他语言的实用模块(第 02 部分):构建受 Python 启发的 REQUESTS 库
在本文中,我们实现了一个类似于 Python 中 requests 模块的功能,以便更轻松地使用 MQL5 在 MetaTrader 5 中发送和接收 Web 请求。
价格行为分析工具开发(第二十八部分):开盘区间突破工具 价格行为分析工具开发(第二十八部分):开盘区间突破工具
交易时段伊始,市场方向往往晦暗不明,唯有价格突破开盘区间后,趋势才逐渐显现。本文将详解如何利用MQL5编写一款EA,自动识别与分析开盘区间突破,为日内交易提供精准、经得起数据验证的入场信号。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
从新手到专家:使用 MQL5 制作动画新闻标题(五)—— 事件提醒系统 从新手到专家:使用 MQL5 制作动画新闻标题(五)—— 事件提醒系统
在本讨论中,我们将探索在整合 News Headline EA 显示的经济日历事件的改进事件警报逻辑时所取得的进一步进展。这项改进至关重要 —— 它能确保用户在重要事件发生前不久及时收到通知。加入此讨论以了解更多信息。