English Русский Deutsch 日本語 Português
preview
MQL5 简介(第 11 部分):MQL5 中使用内置指标的初学者指南(二)

MQL5 简介(第 11 部分):MQL5 中使用内置指标的初学者指南(二)

MetaTrader 5指标 |
609 4
Israel Pelumi Abioye
Israel Pelumi Abioye

概述

欢迎回到我们的 MQL5 系列!我们将在本章讨论一个有趣的主题,我相信你会发现它非常有用和引人入胜。我们在上一章中研究了如何使用 MQL5 的内置指标,特别关注了 RSI。不仅如此,我们甚至还进行了一个实际项目,向您展示了如何轻松地将 RSI 纳入您的交易方法。这次我们更进一步,在我们的项目中纳入了三个强有力的指标(RSI、随机振荡器和移动平均线),而不是一个。这还不是全部;您还将通过我们的项目了解隐藏背离检测的有趣想法。我们将具体讨论如何发现隐藏的看涨和看跌背离。

重要的是要记住,这个项目的唯一目标是教育。建立您使用 MQL5 的信心并学习如何以编程方式识别这些模式是其主要目标。因此,让我们开始这个实用的项目,它将增加您对 MQL5 的理解,并使您能够提高您的交易能力。

在本文中,您将了解:

  • 如何检查 RSI 低点和高点与相应价格走势的关系,以发现隐藏的看涨和看跌背离。
  • 使用多种指标(RSI、MA 和随机振荡指标)开发 EA 交易。
  • 识别并可视化图表上隐藏的看涨和看跌背离。
  • 通过计算基于美元风险的手数大小和使用内置指标时不一致烛形大小的止损距离来实施风险管理。
  • 使用背离信号,按照预定的风险回报率 (RRR) 实现交易自动化。


1.设置项目

1.1.EA 的工作原理

该项目使用移动平均线和 RSI 指标来寻找隐藏的看涨和看跌背离。RSI 可以发现 RSI 水平与相应价格之间的差异,而 MA 则有助于识别趋势。随机振荡指标最终仅用于验证交易信号,而不是用于识别背离。

1.1.1.隐藏的看涨背离

此时,RSI 形成较低的低点 (LL),而价格形成较高的低点 (HL)。这种背离意味着 RSI 较弱,表明未来可能出现上涨势头,尽管匹配价格(C-Price)形成了较高的底部。

图 1. 隐藏的看涨背离

1.1.2.隐藏的看跌背离

此时,RSI 形成较高的高点,而价格形成较低的高点。提示未来可能出现下行势头。

图 2. 隐藏的看跌背离

1.1.3.卖出逻辑

趋势过滤器:

  • 确保当前价格低于 200 EMA,以确认看跌趋势。

标记最后一个重要的 RSI 高点:

  • 确定一根看涨烛形之后形成的最新 RSI 高点,然后是连续 3 根看跌烛形。
  • 在 RSI 图表上的这个水平上放置一条水平线。

标记对应价格:

  • 在价格图表上(切换到折线图),在 RSI 高点的相应价格(C-PRICE)水平放置一条水平线。

隐藏的看跌背离:

  • 寻找 RSI 水平线上方的新 RSI 高点。
  • 确保相应的价格高点低于图表上的价格水平线。

随机震荡指标确认:

  • 满足所有条件后,等待随机震荡指标突破超买水平(例如 80),然后在 11 个柱形内再次突破该水平。
  • 如果 11 个柱形内未确认,则不会进行交易。

设置止损(SL)和止盈(TP):

  • SL:最后一次波动的高点。
  • TP:由风险回报率 (RRR)的输入参数定义。

1.1.4.买入逻辑

趋势过滤器:

  • 确保当前价格高于 200 EMA。

标记最后一个重要的 RSI 低点:

  • 确定在看跌烛形之后形成的最新 RSI 低点,然后是连续 3 个看涨烛形
  • 在 RSI 图表上的这个水平上放置一条水平线。

标记对应价格:

  • 在价格图表上(切换到折线图),在 RSI 低点的相应价格(C-PRICE)水平放置一条水平线。

隐藏的看涨背离:

  • 寻找低于 RSI 水平线的新 RSI 低点。
  • 确保相应的价格低点高于图表上的价格(C-PRICE)水平线。

随机震荡指标确认:

  • 满足所有条件后,等待随机震荡指标跌破超卖水平(例如 20),然后在 11 个柱形内再次跌破超卖水平。
  • 如果 11 个柱形内未确认,则不会进行交易。

设置止损(SL)和止盈(TP):

  • SL:最后一次波动的低点。
  • TP:由风险回报率 (RRR)的输入参数定义。


2.添加指标属性并检索数据

2.1.设置指标属性

我们将设置项目中使用的指标的属性:随机振荡指标、RSI 和移动平均线。这些属性在设计EA时提供了自由,并指定了指标的行为方式。

类比

考虑一下你为特定任务准备了一个工具包。与卷尺类似,移动平均线确保您理解任务的总体尺度(趋势方向)。通过充当放大镜,RSI 可以帮助您发现最小的细微差别或隐藏的背离。在进入下一阶段(最终确认)之前,随机振荡指标作为一个调平工具,确保一切都完美对齐。

将计算机视为您在这项工作中的助手。你必须向它解释这些工具是什么,以及它们应该如何运作,然后才能要求它使用它们。我们无法指望一个通用工具就能知道该做什么。相反,您必须指定每个工具的特性,例如卷尺的类型、放大镜的放大级别或水平工具的灵敏度。通过配置这些功能,计算机可以保证准确地了解要使用哪些指标以及如何有效地使用它们。

示例:
//DECLEAR INDICATOR HANDLES
int ma_handle;
int rsi_handle;
int stoch_handle;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {

//INDICATOR PROPERTIES
   ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE);
   rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE);
   stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH);

   return(INIT_SUCCEEDED);
  }

解释:

想象一下,您身处一个工作室,拥有三件专门用于您项目的设备:卷尺(MA)、放大镜(RSI)和水平仪(随机振荡指标)。在开始之前,确保每个工具都已准备好使用并正确设置。你不会简单地把它们从工具箱里拿出来,现在就使用它们!

声明指标句柄(准备工具)

int ma_handle;
int rsi_handle;
int stoch_handle;

这类似于为车间中存放工具的橱柜命名。每个工具都必须有自己的位置,以便在需要时可以找到它。

  • ma_handle 用于卷尺(移动平均线)。
  • rsi_handle 用于放大镜(RSI)。
  • stoch_handle 用于水平仪(随机振荡指标)。

设置工具(初始化指标)

iMA(移动平均线)设置

ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE);

这就像告诉你的助手,“我需要为一个项目设置卷尺。”您正在指定如何使用它:

  • _Symbol:这可以让助手知道您正在测量什么,例如您正在处理的资产、股票或货币对。这描述了您正在研究的资产(例如, EUR/USD),就像您用卷尺识别所测量的内容一样。
  • PERIOD_CURRENT:这是指您正在处理的时间周期。把它想象成卷尺的长度 —— 您测量的是过去一天、一周还是一个月?PERIOD_CURRENT 表示您正在使用当前图表的时间周期。
  • 200:这是卷尺的长度(移动平均线将覆盖的烛形数量)。因此,您要求您的助手使用卷尺来查看过去 200 个周期来测量总体趋势。
  • 0:这就是偏移。它表示您想要将测量值向右或向左移动多少。这里,0 表示没有偏移 —— 您直接测量当前价格和过去 200 个时期的价格。
  • MODE_EMA:这指定了您使用的卷尺类型:指数移动平均线(EMA)。EMA 比简单移动平均线更敏感,对价格变化的反应更快。因此,您选择的是一种能够快速适应新数据的卷尺。
  • PRICE_CLOSE:这是卷尺的参考点。在这种情况下,你是根据每个时期的收盘价来衡量的。就像卷尺可以校准以测量特定距离一样,你告诉你的助手具体测量收盘价。

图 3. MA 的属性

iRSI(相对强弱指数)设置

rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE);

现在,您正在设置放大镜(RSI)。你告诉你的助手如何使用它:

  • _Symbol:这与之前的工作方式相同,指定您正在分析的资产。
  • PERIOD_CURRENT:就像移动平均线一样,您正在使用当前图表的时间周期。
  • 14:这是 RSI 的周期。把它想象成设置放大镜的放大倍数。您告诉您的助手关注 14 个周期(或烛形),帮助您分析相对强度并检测背离。
  • PRICE_CLOSE:与移动平均线一样,您需要关注收盘价来计算 RSI。这可确保您查看每个时段的终点以获得准确的读数。

图 4. RSI 的属性

iStochastic(随机震荡指标)设置

stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH);

  • _Symbol:再次,这指定了您正在分析的资产,就像以前的指标一样。
  • PERIOD_CURRENT:您正在使用当前图表的时间周期。
  • 5、3 和 3:这些是随机振荡指标的参数:
  • 5 是 %K 周期:这就像设置水平仪(随机振荡指标)对价格变动的敏感度。它检查最近 5 个时期以查看价格相对于其范围的位置。
  • 3 是 %D 周期:这是 %K 的平滑版本,通常用于获得更清晰的信号。 
  • 3 是减速周期:这进一步平滑了指标,以确保它不会对微小的变化反应过度,从而有助于避免错误信号。
  • MODE_SMA:您要求水平仪使用简单移动平均线 (SMA) 方法来平滑 %K 和 %D 值,这是一种基本但可靠的平均数据方法。
  • STO_LOWHIGH:这告诉助手使用低/高方法来计算随机振荡指标。这意味着您关注的是每个时期的价格范围(最低价和最高价),而不是收盘价。

图 5. 振荡器的属性

通过告诉计算机这些参数,您可以确保每个指标(工具)都经过适当的校准,以便在项目中有效准确地执行其工作。


3.检索指标和烛形数据

3.1.检索指标数据

从我们在上一节中建立的指标中提取数据将是本节的主要重点。我们可以通过检索指标数据来为我们的 EA 交易系统的交易建议提供信息。我们还将研究如何从随机振荡指标、RSI 和移动平均线 (MA) 中提取信息。这些因素至关重要,因为它们为我们的 EA 提供了进行交易所需的线索。我们的 EA 必须从这些指标中获取相关数据,以确定是否满足交易条件,就像建筑商需要从他们的设备中进行精确测量以确保项目进展顺利一样。我们将在接下来的阶段中研究如何有效地调用和存储这些变量,以便 EA 可以使用实时市场数据来做出判断。

示例:

//DECLEAR INDICATOR HANDLES
int ma_handle;
int rsi_handle;
int stoch_handle;

//DECLEAR DOUBLE VARIABLES THAT STORES INDICATORS DATA
double ma_buffer[];
double rsi_buffer[];
double stoch_buffer_k[], stoch_buffer_d[];

//DECLEAR VARIABLES TO STORE CANDLE DATA
double open[];
double close[];
double high[];
double low[];
datetime time[];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {

//INDICATOR PROPERTIES
   ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE);
   rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE);
   stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH);

//START FROM THE LATEST CANDLE ON THE CHART
   ArraySetAsSeries(ma_buffer,true);
   ArraySetAsSeries(rsi_buffer,true);
   ArraySetAsSeries(stoch_buffer_k,true);
   ArraySetAsSeries(stoch_buffer_d,true);

//START FROM THE LATEST CANDLE ON THE CHART
   ArraySetAsSeries(open,true);
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(time,true);

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

//COPY INDICATOR'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART
   CopyBuffer(ma_handle,0,1,30,ma_buffer);
   CopyBuffer(stoch_handle,0,1,30,stoch_buffer_k);
   CopyBuffer(stoch_handle,1,1,30,stoch_buffer_d);
   CopyBuffer(rsi_handle,0,1,30,rsi_buffer);

//COPY CANDLE'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART
   CopyOpen(_Symbol,PERIOD_CURRENT,1,30,open);
   CopyClose(_Symbol,PERIOD_CURRENT,1,30,close);
   CopyHigh(_Symbol,PERIOD_CURRENT,1,30,high);
   CopyLow(_Symbol,PERIOD_CURRENT,1,30,low);
   CopyTime(_Symbol,PERIOD_CURRENT,1,30,time);

  }

解释:

声明存储指标数据的双精度型变量

必须先声明数组,然后才能存储指标数据。由于指标的数值(包括随机震荡指标、RSI 和移动平均线)将存储在这些数组中,因此它们属于双精度类型。ma_buffer[] 将保存移动平均线 (MA) 的值,而 rsi_buffer[] 保留 RSI 的值。stoch_buffer_k[] 和 stoch_buffer_d[] 数组专门存储随机指标的 K 线和 D 线。这些数组最初是空的,并将很快填充我们从图形中获得的指标数据。

从图表上的最新烛形开始

在此步骤中,我们使用函数 ArraySetAsSeries() 来排列数组,以便最新的数据位于每个数组的第一个索引中。通过传递 true 作为参数,我们告诉程序从最新的值开始填充数组。这很重要,因为默认情况下,数组是从最旧的数据(第一个元素)填充的,但在这种情况下,我们需要确保首先可以访问最新的指标值。这种安排使我们在做出交易决策时更容易使用最新数据。

将指标数据复制到数组中

一旦数组设置完毕,我们就使用 CopyBuffer() 函数从图表中检索实际的指标数据。CopyBuffer() 函数将数据从指标的缓冲区复制到我们之前声明的数组中。每次调用 CopyBuffer() 都针对一个指标:

  • 对于移动平均线,我们从 ma_handle 复制数据并将其放置在 ma_buffer[] 中。
  • 我们检索随机震荡指标的 %K 和 %D 值,并将它们分别存储在 stoch_buffer_k[] 和 stoch_buffer_d[] 中。
  • 我们将 RSI 的数据复制到 rsi_buffer[] 中。

该函数通过第 4 个输入参数 30 指示从最近 30 个柱传输数据,并通过第 3 个参数从第二近的柱开始传输数据,这使我们能够从指标中提取 EA 交易分析和决策所需的历史数据。

总之,这些步骤确保我们可以从指标中收集、存储和访问数据。通过声明数组、正确设置数据顺序以及使用 CopyBuffer() 检索数据,我们已做好一切准备,可以开始分析指标值并根据这些值做出交易决策。

3.2.烛形数据

我们现在必须从指标中成功检索数据后,收集匹配的烛形数据以匹配指标值。由于烛形数据为我们提供了应用指标的背景,因此它对于交易决策至关重要。蜡烛图向我们展示了真实的市场价格活动,就像指标告诉我们趋势和动量一样。通过整合指标和烛形数据,我们可以找到可能的交易信号,包括看涨或看跌背离的发展或可能表明市场情绪变化的其他模式。

示例:

double open[];
double close[];
double high[];
double low[];
datetime time[];

我们声明数组(open[]、close[]、high[]、low[] 和 time[])来存储基本的烛形信息,例如开盘价、收盘价、最高价和最低价,以及它们对应的时间戳。这些变量有助于我们分析价格走势,并将其与之前检索到的指标数据相关联。

ArraySetAsSeries(open,true);
ArraySetAsSeries(close,true);
ArraySetAsSeries(high,true);
ArraySetAsSeries(low,true);
ArraySetAsSeries(time,true);
为了确保最新的烛形位于每个数组的开头,我们使用 ArraySetAsSeries 函数。这会重新排列数据,因此最新的烛形位于索引 0,较旧的烛形按顺序排列。这种对齐简化了分析并确保与指标数据结构的一致性。
CopyOpen(_Symbol,PERIOD_CURRENT,1,30,open);
CopyClose(_Symbol,PERIOD_CURRENT,1,30,close);
CopyHigh(_Symbol,PERIOD_CURRENT,1,30,high);
CopyLow(_Symbol,PERIOD_CURRENT,1,30,low);
CopyTime(_Symbol,PERIOD_CURRENT,1,30,time);

使用 CopyOpen、CopyClose、CopyHigh、CopyLow 和 CopyTime 等函数,我们用实际市场数据填充数组。我们从图表上的第 2 根烛形开始检索最近 30 根烛形的数据,将价格数据与相应的指标值对齐,以便做出准确的决策。


4.利用相应数据识别最新的 RSI 高点和低点

作为将指标行为与价格行为进行比较的标准,确定最近的 RSI 高点和低点至关重要。当存在隐藏的看涨背离时,市场可能会继续上涨,这种背离发生在价格创下更高的低点,RSI 创下更低的低点时。然而,当价格达到较低的高点,RSI 达到较高的高点时,存在一个隐藏的看跌背离,表明可能会持续下跌。

为了确定当时的高点是否低于均线,或者低点是否高于均线,我们还将获得匹配的移动平均数。这种额外的验证可确保趋势符合预期的走势,并有助于验证背离信号。

4.1.寻找隐藏的看涨背离

识别最新的 RSI 低点是检测隐藏的看涨背离的关键一步。RSI 低点表示在价格回撤期间动量减弱的点,将其与相应的价格和移动平均线(MA)值一起分析可以提供有价值的见解。当 RSI 形成较低的低点,而价格形成较高的低点时,就会出现隐藏的看涨背离,这表明尽管存在回撤,但仍有潜在的上涨势头。

为了检测 RSI 低点,我们将定义一个特定的逻辑:低点必须在至少一次看跌运动之后形成,然后是连续三次看涨运动。这种情况确保了低点反映出动量的重大转变。一旦确定了 RSI 低点,我们将检索其位置,记下图表上该点的对应价格,并提取移动平均值以确定价格低点是否高于 MA。这种验证有助于确认背离信号与当前趋势的一致性,从而提高其可靠性。

在确定 RSI 低点后,我们还将定义逻辑来搜索 RSI 低点之后可能形成的较低低点。这个较低的低点必须有一个低于确定的 RSI 低点的 RSI 值,此时的价格仍高于之前的价格低点。这确保了背离保持不变并支持隐藏的看涨背离标准。通过整合这些步骤,我们建立了一种全面而结构化的方法来可靠地识别隐藏的看涨背离机会。

示例:
//DECLEAR INDICATOR HANDLES
int ma_handle;
int rsi_handle;
int stoch_handle;

//DECLEAR DOUBLE VARIABLES THAT STORES INDICATORS DATA
double ma_buffer[];
double rsi_buffer[];
double stoch_buffer_k[], stoch_buffer_d[];

//DECLEAR VARIABLES TO STORE CANDLE DATA
double open[];
double close[];
double high[];
double low[];
datetime time[];

//DECLEAR VARIBLE ROR RSI LOW AND CORESPONDING  CANDLE
double   rsi_low_value;          // Stores the RSI value at the identified low point.
double   corresponding_low_value; // Stores the price (closing value) corresponding to the RSI low.
double   corresponding_low_ma;    // Stores the Moving Average value at the same point.
datetime rsi_low_time;           // Stores the timestamp of the RSI low.

//DECLEAR VARIABLE FOR THE MINIMUM CORRESPONDING PRICE VALUE
int  minimum_value_low;

//DECLEAR VARIBLE ROR NEW RSI LOW AND CORESPONDING  CANDLE
double new_rsi_low_value;
datetime new_rsi_low_time;
double new_corresponding_low_value;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {

//INDICATOR PROPERTIES
   ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE);
   rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE);
   stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH);

//START FROM THE LATEST CANDLE ON THE CHART
   ArraySetAsSeries(ma_buffer,true);
   ArraySetAsSeries(rsi_buffer,true);
   ArraySetAsSeries(stoch_buffer_k,true);
   ArraySetAsSeries(stoch_buffer_d,true);

//START FROM THE LATEST CANDLE ON THE CHART
   ArraySetAsSeries(open,true);
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(time,true);

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

//COPY INDICATOR'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART
   CopyBuffer(ma_handle,0,1,30,ma_buffer);
   CopyBuffer(stoch_handle,0,1,30,stoch_buffer_k);
   CopyBuffer(stoch_handle,1,1,30,stoch_buffer_d);
   CopyBuffer(rsi_handle,0,1,30,rsi_buffer);

//COPY CANDLE'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART
   CopyOpen(_Symbol,PERIOD_CURRENT,1,30,open);
   CopyClose(_Symbol,PERIOD_CURRENT,1,30,close);
   CopyHigh(_Symbol,PERIOD_CURRENT,1,30,high);
   CopyLow(_Symbol,PERIOD_CURRENT,1,30,low);
   CopyTime(_Symbol,PERIOD_CURRENT,1,30,time);

//LOOP THROUGH THE LAST 30 BARS ON THE CHART
   for(int i = 0; i < 30; i++)
     {
      //PREVENT ARRAY OUT OF RANGE ERROR
      if((i+1 < 30) && (i+2 < 30) && (i < 30) && (i+3 < 30) && (i+4 < 30))
        {
         //LOGIC TO IDENTIFY THE LATEST RSI LOW
         if(rsi_buffer[i+4] > rsi_buffer[i+3] && rsi_buffer[i+2] > rsi_buffer[i+3] && rsi_buffer[i+1] > rsi_buffer[i+2] && rsi_buffer[i] > rsi_buffer[i+1])
           {
            //GETTING LATEST RSI LOW, CORRESPONDING CANDLE LOW, CORRESPONDING MA VALUE, and RSI TIME
            rsi_low_value = rsi_buffer[i+3];
            corresponding_low_value = close[i+3];
            corresponding_low_ma = ma_buffer[i+3];
            rsi_low_time = time[i+3];

            break;
           }

        }
     }

//TOTAL NUMBERS OF BARS FROM THE LAST SIGNIFICANT RSI LOW
   int total_bars_2 = 0;
   total_bars_2 = Bars(_Symbol,PERIOD_CURRENT,rsi_low_time, time[0]);

//MINIMUM CLOSE PRICE FROM THE LAST SIGNIFICANT RSI LOW
   minimum_value_low = ArrayMinimum(close,0,total_bars_2);

   if(corresponding_low_value > corresponding_low_ma && close[0] > ma_buffer[0] && close[minimum_value_low] >= corresponding_low_value)
     {
      //CREATE LINES TO MARK RSI AND CORRESPONDING CANDLE LOW
      ObjectCreate(ChartID(),"RSI LOW VALUE",OBJ_TREND,1,rsi_low_time,rsi_low_value,TimeCurrent(),rsi_low_value);
      ObjectCreate(ChartID(),"C-CANDLE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,TimeCurrent(),corresponding_low_value);
      //SETTING OBJECTS COLOUR
      ObjectSetInteger(ChartID(),"RSI LOW VALUE",OBJPROP_COLOR,clrBlack);
      ObjectSetInteger(ChartID(),"C-CANDLE",OBJPROP_COLOR,clrBlack);

      //CREATE TWO LINES TO CONNECT RSI LOW AND THE CORRESPONDING PRICE ON THE CHART
      ObjectCreate(ChartID(),"C-CANDLE LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,rsi_low_time,0);
      ObjectCreate(ChartID(),"RSI LOW LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,rsi_low_time,100);
      //SETTING OBJECTS COLOUR
      ObjectSetInteger(ChartID(),"C-CANDLE LINE",OBJPROP_COLOR,clrBlack);
      ObjectSetInteger(ChartID(),"RSI LOW LINE",OBJPROP_COLOR,clrBlack);

      //CREATE TEXTS TO MART RSI LOW AND CORRESPONDING PRICE (C-PRICE)
      ObjectCreate(ChartID(),"C-CANDLE TEXT",OBJ_TEXT,0,TimeCurrent(),corresponding_low_value);
      ObjectSetString(ChartID(),"C-CANDLE TEXT",OBJPROP_TEXT,"C-PRICE");
      ObjectCreate(ChartID(),"RSI LOW TEXT",OBJ_TEXT,1,TimeCurrent(),rsi_low_value);
      ObjectSetString(ChartID(),"RSI LOW TEXT",OBJPROP_TEXT,"RSI LOW");
      //SETTING TEXT COLOUR
      ObjectSetInteger(ChartID(),"C-CANDLE TEXT",OBJPROP_COLOR,clrBlack);
      ObjectSetInteger(ChartID(),"RSI LOW TEXT",OBJPROP_COLOR,clrBlack);

      //19 LOGIC TO GET THE NEW RSI LOW
      if(rsi_buffer[0] > rsi_buffer[1] && rsi_buffer[1] < rsi_buffer[2] && rsi_buffer[1] < rsi_low_value && close[1] > corresponding_low_value)
        {
         new_rsi_low_value = rsi_buffer[1];
         new_rsi_low_time = time[1];
         new_corresponding_low_value = close[1];

        }

      //CONDITION FOR HIDDEN BULLISH DIVERGENCE
      if(rsi_low_value > new_rsi_low_value && corresponding_low_value < new_corresponding_low_value)
        {
         ObjectCreate(ChartID(),"RSI LOW TREND LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,new_rsi_low_time,new_rsi_low_value);
         ObjectCreate(ChartID(),"L",OBJ_TEXT,1,rsi_low_time,rsi_low_value);
         ObjectSetString(ChartID(),"L",OBJPROP_TEXT,"L");
         ObjectSetInteger(ChartID(),"L",OBJPROP_FONTSIZE,15);

         ObjectSetInteger(ChartID(),"RSI LOW TREND LINE",OBJPROP_COLOR,clrBlack);
         ObjectSetInteger(ChartID(),"L",OBJPROP_COLOR,clrBlack);

         ObjectCreate(ChartID(),"LL",OBJ_TEXT,1,new_rsi_low_time,new_rsi_low_value);
         ObjectSetString(ChartID(),"LL",OBJPROP_TEXT,"LL");
         ObjectSetInteger(ChartID(),"LL",OBJPROP_FONTSIZE,15);

         ObjectSetInteger(ChartID(),"LL",OBJPROP_COLOR,clrBlack);

         //for candle
         ObjectCreate(ChartID(),"C-CANDLE TREND LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,new_rsi_low_time,new_corresponding_low_value);
         ObjectCreate(ChartID(),"CL",OBJ_TEXT,0,rsi_low_time,corresponding_low_value);
         ObjectSetString(ChartID(),"CL",OBJPROP_TEXT,"L");
         ObjectSetInteger(ChartID(),"CL",OBJPROP_FONTSIZE,15);
         ObjectSetInteger(ChartID(),"C-CANDLE TREND LINE",OBJPROP_COLOR,clrBlack);
         ObjectSetInteger(ChartID(),"CL",OBJPROP_COLOR,clrBlack);

         ObjectCreate(ChartID(),"CLL",OBJ_TEXT,0,new_rsi_low_time,new_corresponding_low_value);
         ObjectSetString(ChartID(),"CLL",OBJPROP_TEXT,"HL");
         ObjectSetInteger(ChartID(),"CLL",OBJPROP_FONTSIZE,15);
         ObjectSetInteger(ChartID(),"CLL",OBJPROP_COLOR,clrBlack);

        }

     }

//LOGIC TO DELETE THE OBJECTS WHEN ITS NO LONGER RELEVEANT
   else
      if((close[0] < ma_buffer[0]) || (close[minimum_value_low] < corresponding_low_value))
        {

         ObjectDelete(ChartID(),"RSI LOW VALUE");
         ObjectDelete(ChartID(),"C-CANDLE");
         ObjectDelete(ChartID(),"C-CANDLE LINE");
         ObjectDelete(ChartID(),"RSI LOW LINE");
         ObjectDelete(ChartID(),"C-CANDLE TEXT");
         ObjectDelete(ChartID(),"RSI LOW TEXT");
         ObjectDelete(ChartID(),"RSI LOW TREND LINE");
         ObjectDelete(ChartID(),"L");
         ObjectDelete(ChartID(),"LL");
         ObjectDelete(ChartID(),"C-CANDLE TREND LINE");
         ObjectDelete(ChartID(),"CL");
         ObjectDelete(ChartID(),"RSI LOW TEXT");
         ObjectDelete(ChartID(),"CLL");

        }

}

解释:

可以使用 rsi_low_value 来发现潜在的背离,它记录给定范围内的最低 RSI 值。由于折线图基于收盘价,因此通过相应的低值(即 RSI 低点出现的烛形的收盘价)来确保与它们的兼容性。当转换为折线图时,这种对齐使 RSI 低点和相关价格点看起来合乎逻辑。为了确定价格是高于还是低于移动平均线(MA),移动平均线经常验证趋势,corresponding_low_ma 将 MA 值保存在同一位置。最后,rsi_low_time 函数允许通过记录 RSI 低点的精确时间来构建与特定烛形相关的趋势线或标签等对象。

下一步是确定最后一个显著的 RSI 低点。这涉及检查 RSI 值的特定模式,其中至少有一个看跌运动,然后是连续三个看涨运动。

  1. 代码扫描最后 30 个柱形以查找显著的 RSI 低点。
  2. 关键条件:
    • i+3 处的 RSI 值必须首先下降(从 i+4 到 i+3 的看跌走势)。
    • 此后,它必须显示三个连续的增加(i+3 → i+2 → i+1 → i)。
  3. 一旦检测到该模式,就会保存 RSI 低点、相应的收盘价、移动平均值和时间戳。

这一逻辑确保了低点是有意义的 RSI 行为的一部分,RSI 下降后是复苏,使其成为一个重要的转折点。

为什么使用 close[i+3]?

如果您使用折线图,则使用收盘价绘制折线。通过将 RSI 低点与收盘价相关联,您创建的标记(例如趋势线)将与折线图正确对齐。这确保了清晰度和视觉一致性。

if(corresponding_low_value > corresponding_low_ma && close[0] > ma_buffer[0] && close[minimum_value_low] >= corresponding_low_value)

此条件检查几个因素,以确认当前情况是否符合检测隐藏看涨背离的标准:

  • corresponding_low_value > corresponding_low_ma:这将检查已识别的 RSI 低点的价格是否高于移动平均线 (MA)。这表明价格高于其移动平均线,这表明趋势仍然相对强劲或看涨。
  • close[0] > ma_buffer[0]:这将检查最近的收盘价(当前烛形)是否高于移动平均值,从而确认价格仍高于 MA 且处于上升趋势。
  • close[minimum_value_low] >= corresponding_low_value:这证实了最近显著低点(之前发现的低点)的价格仍然处于或高于之前显著低点。这有助于确保价格不会跌破之前的低点,表明价格低点更高并支持看涨的解读。

当所有这些条件都成立时,这表明价格高于其移动平均线,并形成了一个更高的低点,这是潜在隐藏看涨背离的一个关键特征,在这种背离中,价格行为并没有证实 RSI 所暗示的更深层次的看跌势头。

//CREATE LINES TO MARK RSI AND CORRESPONDING PRICE
ObjectCreate(ChartID(),"RSI LOW VALUE",OBJ_TREND,1,rsi_low_time,rsi_low_value,TimeCurrent(),rsi_low_value);
ObjectCreate(ChartID(),"C-CANDLE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,TimeCurrent(),corresponding_low_value);
//SETTING OBJECTS COLOUR
ObjectSetInteger(ChartID(),"RSI LOW VALUE",OBJPROP_COLOR,clrBlack);
ObjectSetInteger(ChartID(),"C-CANDLE",OBJPROP_COLOR,clrBlack);

//CREATE TWO LINES TO CONNECT RSI LOW AND THE CORRESPONDING PRICE ON THE CHART
ObjectCreate(ChartID(),"C-CANDLE LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,rsi_low_time,0);
ObjectCreate(ChartID(),"RSI LOW LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,rsi_low_time,100);
//SETTING OBJECTS COLOUR
ObjectSetInteger(ChartID(),"C-CANDLE LINE",OBJPROP_COLOR,clrBlack);
ObjectSetInteger(ChartID(),"RSI LOW LINE",OBJPROP_COLOR,clrBlack);

//CREATE TEXTS TO MART RSI LOW AND CORRESPONDING PRICE (C-PRICE)
ObjectCreate(ChartID(),"C-CANDLE TEXT",OBJ_TEXT,0,TimeCurrent(),corresponding_low_value);
ObjectSetString(ChartID(),"C-CANDLE TEXT",OBJPROP_TEXT,"C-PRICE");
ObjectCreate(ChartID(),"RSI LOW TEXT",OBJ_TEXT,1,TimeCurrent(),rsi_low_value);
ObjectSetString(ChartID(),"RSI LOW TEXT",OBJPROP_TEXT,"RSI LOW");
//SETTING TEXT COLOUR
ObjectSetInteger(ChartID(),"C-CANDLE TEXT",OBJPROP_COLOR,clrBlack);
ObjectSetInteger(ChartID(),"RSI LOW TEXT",OBJPROP_COLOR,clrBlack);

此代码在图表上创建视觉元素,以突出显示 RSI 低点及其相应价格,这是识别潜在背离模式的关键点。首先,绘制趋势线来标记同一点的 RSI 低值和相应的价格(C-PRICE)。这些标有 “RSI LOW VALUE” 和 “C-CANDLE” 的线条以黑色绘制,以保持视觉清晰度。这些趋势线的目的是将 RSI 低点与相应的价格直观地连接起来,帮助您轻松识别价格行为与 RSI 行为之间的关系。

接下来,创建额外的趋势线,将 RSI 低点连接到图表上的相应价格。“C-CANDLE LINE” 将 RSI 低点的价格与当前时间连接起来,而 “RSI LOW LINE” 则在图表上垂直延伸 RSI 低点。这些线作为视觉标记,有助于分析 RSI 和价格的走势,特别是在寻找潜在的看涨或看跌背离时。所有这些趋势线都设置为黑色,以保持一致性,并确保它们在图表背景下清晰可见。

最后,代码创建文本标签,用 RSI 低点和相应价格(烛形低点)的值来注释图表。标签 “RSI LOW” 和 “C-PRICE” 分别放置在 RSI 和价格的相应水平,从而可以轻松地直接在图表上识别这些关键点。这些文本标签也设置为黑色,以确保它们突出且清晰易读。通过创建这些对象,该代码可以帮助交易者快速可视化重要的价格水平和 RSI 点,帮助进行技术分析和识别隐藏的背离。

输出:

图 6. RSI 低点和对应的价格

一旦确定了第一个显著的 RSI 低点,下一步就是找到一个新的更低的 RSI 低点。这里的逻辑比第一个低点更简单。

RSI 新低的逻辑


对于新的 RSI 低点:

  1. 必须只有一个看跌运动,RSI 下降一个柱。
  2. 随后必须出现一个看涨运动,RSI 在低点后立即开始上升。
if(rsi_buffer[0] > rsi_buffer[1] && rsi_buffer[1] < rsi_buffer[2] && rsi_buffer[1] < rsi_low_value && close[1] > corresponding_low_value)
  {
  new_rsi_low_value = rsi_buffer[1];
  new_rsi_low_time = time[1];
  new_corresponding_low_value = close[1];

  }

代码的第一部分声明变量来存储有关新 RSI 低点及其相应价格水平的信息。new_rsi_low_value 变量保存新的 RSI 低值,这对于比较和分析背离是必要的。new_rsi_low_time 存储新的 RSI 低点出现的时间,这个时间戳对于在图表上标记这个点很重要。最后,new_corresponding_low_value 存储与新的 RSI 低点相关的价格,通常是收盘价,这有助于将 RSI 走势与实际市场价格行为联系起来。

确定新 RSI 低点的要求在代码的第二部分中定义。这一推理保证了新低点满足两个要求:看跌运动(RSI 下降)和看涨运动(RSI 开始上升)。此外,匹配价格必须产生更高的低点,这表明可能存在潜在的看涨背离,而新的 RSI 低点必须低于之前的主要 RSI 低点。为了强调图表上的背离,如果满足这些条件,算法会为 RSI 低点、其时间戳和相关价格分配新值。

if(rsi_low_value > new_rsi_low_value && corresponding_low_value < new_corresponding_low_value)

这种情况寻找价格图表和 RSI 之间的隐藏看涨背离。当价格出现更高的低点,而 RSI 出现更低的低点时,这被称为隐藏的看涨背离。

//FOR RSI
ObjectCreate(ChartID(),"RSI LOW TREND LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,new_rsi_low_time,new_rsi_low_value);
ObjectCreate(ChartID(),"L",OBJ_TEXT,1,rsi_low_time,rsi_low_value);
ObjectSetString(ChartID(),"L",OBJPROP_TEXT,"L");
ObjectSetInteger(ChartID(),"L",OBJPROP_FONTSIZE,15);
ObjectSetInteger(ChartID(),"RSI LOW TREND LINE",OBJPROP_COLOR,clrBlack);
ObjectSetInteger(ChartID(),"L",OBJPROP_COLOR,clrBlack);
ObjectCreate(ChartID(),"LL",OBJ_TEXT,1,new_rsi_low_time,new_rsi_low_value);
ObjectSetString(ChartID(),"LL",OBJPROP_TEXT,"LL");
ObjectSetInteger(ChartID(),"LL",OBJPROP_FONTSIZE,15);
ObjectSetInteger(ChartID(),"LL",OBJPROP_COLOR,clrBlack);

//FOR CORRESPONDING PRICE
ObjectCreate(ChartID(),"C-CANDLE TREND LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,new_rsi_low_time,new_corresponding_low_value);
ObjectCreate(ChartID(),"CL",OBJ_TEXT,0,rsi_low_time,corresponding_low_value);
ObjectSetString(ChartID(),"CL",OBJPROP_TEXT,"L");
ObjectSetInteger(ChartID(),"CL",OBJPROP_FONTSIZE,15);
ObjectSetInteger(ChartID(),"C-CANDLE TREND LINE",OBJPROP_COLOR,clrBlack);
ObjectSetInteger(ChartID(),"CL",OBJPROP_COLOR,clrBlack);
ObjectCreate(ChartID(),"CLL",OBJ_TEXT,0,new_rsi_low_time,new_corresponding_low_value);
ObjectSetString(ChartID(),"CLL",OBJPROP_TEXT,"HL");
ObjectSetInteger(ChartID(),"CLL",OBJPROP_FONTSIZE,15);
ObjectSetInteger(ChartID(),"CLL",OBJPROP_COLOR,clrBlack);

一旦识别出隐藏的看涨背离,这段代码旨在以图形方式标记重要的图表方面。在第一组行动中,RSI 是主要重点。第一个重要的 RSI 低点和随后的较低 RSI 低点由趋势线和文本标签表示。代码最初在新的较低 RSI 低点(new_rsi_low_value)和原始 RSI 低点(rsi_low_value)之间建立一条趋势线。得益于趋势线,交易者可以更容易地发现 RSI 的下降趋势。在这些情况下,还会添加文本标签 “L” 和 “LL” 以将其标识为显著的低点;“L” 表示初始低点,“LL” 表示较低的低点。

下一组行动将集中在相应的价格水平上。与 RSI 类似,创建趋势线和文本标签以将第一个价格低点(corresponding_low_value)连接到新的价格低点(new_corresponding_low_value)。相应的价格预计将形成更高的低点,这对隐藏的看涨背离设置至关重要。代码在这两个价格点之间创建一条趋势线,并用 “L” 和 “HL” 标记它们,其中 “L” 标记第一个显著的价格低点,“HL” 表示价格形成的更高的低点。这些视觉元素可帮助您确认价格走势显示预期的较高低点,而 RSI 显示较低的低点,这是隐藏看涨背离的经典指标。

当隐藏的看涨背离条件不再有效时,ObjectDelete() 函数负责清理图表上的可视对象。具体来说,它会检查当前收盘价是否低于移动平均线(ma_buffer[0])或者自上次显著 RSI 低点以来的最低收盘价是否低于相应的价格低点(corresponding_low_value)。如果满足这些条件中的任何一个,则删除之前绘制的趋势线、文本标签和其他标记 RSI 和价格低点的对象。这确保了图表中没有过时或无关的视觉标记,使交易者能够专注于最新和最有效的市场状况。

输出:

图 7. 低点和新低

4.2.寻找隐藏的看跌背离

当 RSI 形成更高的高点而价格创造更低的高点时,就会出现隐藏的看跌背离,这表明尽管价格暂时复苏,但仍有潜在的下行势头。为了识别这种模式,使用特定的逻辑检测最新的 RSI 高点:它必须发生在至少一次看涨运动之后,然后是连续三次看跌运动,这表明动量达到了有意义的峰值。一旦识别出 RSI 高点,就会检索其相应的价格和移动平均线(MA)值,以确认与当前看跌趋势的一致性。有效的隐性看跌背离要求价格高点保持在均线以下,从而强化了下行倾向。

与隐藏的看涨背离不同,RSI 形成较低的低点,而价格则形成较高的低点(表明潜在的上涨势头),隐藏的看跌背离则相反。在识别出 RSI 高点后,该逻辑还包括检查超过第一个 RSI 高点的新 RSI 高点,相应的价格形成较低的高点。这种背离凸显了看涨势头的减弱,而隐藏的看涨背离则表明看跌势头的减弱。这些区别确保了一种结构化的方法来有效地检测和区分这两种类型的背离。

示例:

//DECLEAR VARIBLE ROR RSI HIGH AND CORESPONDING  PRICE
double   rsi_high_value;
double   corresponding_high_value;
double   corresponding_high_ma;
datetime rsi_high_time;

//DECLEAR VARIABLE FOR THE MAXIMUM CORRESPONDING PRICE VALUE
int maximum_value_high;

// DECLEAR VARIBLE ROR NEW RSI HIGH AND CORESPONDING PRICE
double new_rsi_high_value;
datetime new_rsi_high_time;
double new_corresponding_high_value;
//LOOP THROUGH THE LAST 30 BARS ON THE CHART
for(int i = 0; i < 30; i++)
  {
//PREVENT ARRAY OUT OF RANGE ERROR
   if((i+1 < 30) && (i+2 < 30) && (i < 30) && (i+3 < 30) && (i+4 < 30))
     {
      //LOGIC TO IDENTIFY THE LATEST RSI HIGH
      if(rsi_buffer[i+4] < rsi_buffer[i+3] && rsi_buffer[i+2] < rsi_buffer[i+3] && rsi_buffer[i+1] < rsi_buffer[i+2] && rsi_buffer[i] < rsi_buffer[i+1])
        {
         //GETTING LATEST RSI HIGH, CORRESPONDING PRICE, CORRESPONDING MA VALUE, and RSI TIME
         rsi_high_value = rsi_buffer[i+3];
         corresponding_high_value = close[i+3];
         corresponding_high_ma = ma_buffer[i+3];
         rsi_high_time = time[i+3];

         break;
        }

     }
  }

//TOTAL NUMBERS OF BARS FROM THE LAST SIGNIFICANT RSI HIGH
int total_bars_3 = 0;
total_bars_3 = Bars(_Symbol,PERIOD_CURRENT,time[0],rsi_high_time);

//MAXIMUM CLOSE PRICE FROM THE LAST SIGNIFICANT RSI LOW
maximum_value_high = ArrayMaximum(close,0,total_bars_3);

if(corresponding_high_value < corresponding_high_ma && close[0] < ma_buffer[0] && close[maximum_value_high] <= corresponding_high_value)
  {
//CREATE LINES TO MARK RSI AND CORRESPONDING PRICE
   ObjectCreate(ChartID(),"RSI HIGH VALUE",OBJ_TREND,1,rsi_high_time,rsi_high_value,TimeCurrent(),rsi_high_value);
   ObjectCreate(ChartID(),"C-CANDLE HIGH",OBJ_TREND,0,rsi_high_time,corresponding_high_value,TimeCurrent(),corresponding_high_value);
//SETTING OBJECTS COLOUR
   ObjectSetInteger(ChartID(),"RSI HIGH VALUE",OBJPROP_COLOR,clrBlack);
   ObjectSetInteger(ChartID(),"C-CANDLE HIGH",OBJPROP_COLOR,clrBlack);

//CREATE TWO LINES TO CONNECT RSI HIGH AND THE CORRESPONDING PRICE ON THE CHART
   ObjectCreate(ChartID(),"C-CANDLE LINE HIGH",OBJ_TREND,0,rsi_high_time,corresponding_high_value,rsi_high_time,0);
   ObjectCreate(ChartID(),"RSI HIGH LINE",OBJ_TREND,1,rsi_high_time,rsi_high_value,rsi_high_time,100);
//SETTING OBJECTS COLOUR
   ObjectSetInteger(ChartID(),"C-CANDLE LINE HIGH",OBJPROP_COLOR,clrBlack);
   ObjectSetInteger(ChartID(),"RSI HIGH LINE",OBJPROP_COLOR,clrBlack);

//CREATE TEXTS TO MART RSI HIGH AND CORRESPONDING PRICE (C-PRICE)
   ObjectCreate(ChartID(),"C-CANDLE TEXT HIGH",OBJ_TEXT,0,TimeCurrent(),corresponding_high_value);
   ObjectSetString(ChartID(),"C-CANDLE TEXT HIGH",OBJPROP_TEXT,"C-PRICE");
   ObjectCreate(ChartID(),"RSI HIGH TEXT",OBJ_TEXT,1,TimeCurrent(),rsi_high_value);
   ObjectSetString(ChartID(),"RSI HIGH TEXT",OBJPROP_TEXT,"RSI HIGH");

//SETTING TEXT COLOUR
   ObjectSetInteger(ChartID(),"C-CANDLE TEXT HIGH",OBJPROP_COLOR,clrBlack);
   ObjectSetInteger(ChartID(),"RSI HIGH TEXT",OBJPROP_COLOR,clrBlack);

//LOGIC TO GET THE NEW RSI HIGH
   if(rsi_buffer[0] < rsi_buffer[1] && rsi_buffer[1] > rsi_buffer[2] && rsi_buffer[1] > rsi_high_value && close[1] < corresponding_high_value)
     {

      new_rsi_high_value = rsi_buffer[1];
      new_rsi_high_time = time[1];
      new_corresponding_high_value = close[1];

     }

   if(rsi_high_value < new_rsi_high_value && corresponding_high_value > new_corresponding_high_value)
     {
      //for rsi
      ObjectCreate(ChartID(),"RSI HIGH TREND LINE",OBJ_TREND,1,rsi_high_time,rsi_high_value,new_rsi_high_time,new_rsi_high_value);
      ObjectCreate(ChartID(),"H",OBJ_TEXT,1,rsi_high_time,rsi_high_value);
      ObjectSetString(ChartID(),"H",OBJPROP_TEXT,"H");
      ObjectSetInteger(ChartID(),"H",OBJPROP_FONTSIZE,15);
      ObjectSetInteger(ChartID(),"RSI HIGH TREND LINE",OBJPROP_COLOR,clrBlack);
      ObjectSetInteger(ChartID(),"H",OBJPROP_COLOR,clrBlack);
      ObjectCreate(ChartID(),"HH",OBJ_TEXT,1,new_rsi_high_time,new_rsi_high_value);
      ObjectSetString(ChartID(),"HH",OBJPROP_TEXT,"HH");
      ObjectSetInteger(ChartID(),"HH",OBJPROP_FONTSIZE,15);
      ObjectSetInteger(ChartID(),"HH",OBJPROP_COLOR,clrBlack);

      //for candle
      ObjectCreate(ChartID(),"C-CANDLE TREND LINE HIGH",OBJ_TREND,0,rsi_high_time,corresponding_high_value,new_rsi_high_time,new_corresponding_high_value);
      ObjectCreate(ChartID(),"CH",OBJ_TEXT,0,rsi_high_time,corresponding_high_value);
      ObjectSetString(ChartID(),"CH",OBJPROP_TEXT,"H");
      ObjectSetInteger(ChartID(),"CH",OBJPROP_FONTSIZE,15);
      ObjectSetInteger(ChartID(),"C-CANDLE TREND LINE HIGH",OBJPROP_COLOR,clrBlack);
      ObjectSetInteger(ChartID(),"CH",OBJPROP_COLOR,clrBlack);
      ObjectCreate(ChartID(),"CLH",OBJ_TEXT,0,new_rsi_high_time,new_corresponding_high_value);
      ObjectSetString(ChartID(),"CLH",OBJPROP_TEXT,"LH");
      ObjectSetInteger(ChartID(),"CLH",OBJPROP_FONTSIZE,15);
      ObjectSetInteger(ChartID(),"CLH",OBJPROP_COLOR,clrBlack);
     }

  }

//LOGIC TO DELETE THE OBJECTS WHEN ITS NO LONGER RELEVEANT
else
   if((close[0] > ma_buffer[0]) || (close[maximum_value_high] > corresponding_high_value))
     {

      ObjectDelete(ChartID(),"RSI HIGH VALUE");
      ObjectDelete(ChartID(),"C-CANDLE HIGH");
      ObjectDelete(ChartID(),"C-CANDLE LINE HIGH");
      ObjectDelete(ChartID(),"RSI HIGH LINE");
      ObjectDelete(ChartID(),"C-CANDLE TEXT HIGH");
      ObjectDelete(ChartID(),"RSI HIGH TEXT");
      ObjectDelete(ChartID(),"RSI HIGH TREND LINE");
      ObjectDelete(ChartID(),"H");
      ObjectDelete(ChartID(),"HH");
      ObjectDelete(ChartID(),"C-CANDLE TREND LINE HIGH");
      ObjectDelete(ChartID(),"CH");
      ObjectDelete(ChartID(),"CLH");

     }

解释:

该代码段旨在通过检测 RSI 高点及其对应的价格高点来识别隐藏的看跌背离,遵循与 4.1 类似的逻辑。寻找隐藏的看涨背离。这里不再过多强调,因为大部分逻辑已经在 4.1 中解释过了。寻找隐藏的看涨背离;唯一的区别是,现在的重点是相反的情况。虽然基本方法保持一致,但此实施方式通过关注 RSI 高点而不是低点来瞄准看跌势头。

代码的第一部分声明了 rsi_high_value、corresponding_high_value 和 corresponding_high_ma 等变量,分别用于存储 RSI 最高值、其对应的价格和移动平均线 (MA) 值。这些用于通过遍历最后 30 个柱并应用一个看涨运动后连续三个看跌运动的条件来识别最新的 RSI 高点。一旦发现 RSI 高点,就会检索相应的价格和 MA 值,并记录时间戳(rsi_high_time)。验证步骤确保相应的价格高点低于均线,从而强化看跌趋势。

图 8. RSI 和相应的高点

接下来,我们检测到一个新的 RSI 高点,RSI 形成一个更高的高点,价格形成一个更低的高点(LH)。这符合隐藏看跌背离的标准,表明价格走势的上行势头减弱。如果满足此条件,则会创建趋势线和标签,以在图表上直观地标记 RSI 高点(H 和 HH)及其对应的价格高点(H 和 LH)。这些视觉标记帮助交易者快速识别潜在的隐藏看跌背离点。

该代码还包括在隐藏看跌背离的条件无效时删除对象的逻辑,例如当当前价格收盘高于 MA 或最高价格时,因为识别的 RSI 高点超过了相应的高点。这确保图表保持清晰并仅反映有效的背离信号。

图 9. 高点和新高


5.结合随机震荡指标并执行交易

现在我们了解了如何使用移动平均线和 RSI 来寻找隐藏的看涨和看跌背离,随机震荡指标作为最后的确认工具。通过确保背离与市场动量的变化相对应,振荡指标可以提高交易进入的精确度。在严格的时间和风险管理指导下,两种背离类型的确认原理取决于振荡指标在超买和超卖水平方面的表现。

5.1.纳入随机震荡指标

在 RSI 和价格走势确认隐藏的看涨背离后,我们将监测随机振荡指标跌破超卖水平 (20),然后再次攀升至该水平之上。该交叉必须在检测到背离后 11 个柱形出现才能验证信号。如果确认,将开始买入交易,止损(SL)为最新的波动低点。止盈(TP)将由预先定义的风险回报率(RRR)确定,以保证严格的交易管理。

示例:

if(total_bars < 11)
  {
   if(stoch_buffer_k[0] < 20 && stoch_buffer_k[1] > 20 && currentBarTime != lastTradeBarTime && totalPositions < 1)
     {
      //
     }
  }

解释:

此代码通过使用随机振荡指标验证来确认隐藏的看涨背离设置。首先,它使用 Bars 函数计算自 RSI 低点 (new_rsi_low_time) 以来的柱数,并确保交易设置在 11 个柱的窗口内保持有效 “(if(total_bars < 11))”。在此期间,它会检查随机震荡指标的 %K 线是否已突破超卖水平 20(stoch_buffer_k[1] < 20 且 stoch_buffer_k[0] > 20)。这一交叉证实了潜在的上涨势头,加强了看涨信号。如果满足这两个条件,代码将继续执行交易逻辑,确保满足隐藏看涨背离的所有标准。

同样,我们将等待随机振荡指标升至超买水平(80)上方,然后再次跌破该水平以确认隐藏的看跌背离。此外,这种看跌交叉需要在检测到背离后的 11 个柱形内发生。如果得到验证,则进行卖出交易,使用指定的 RRR 计算 TP,并将 SL 设置为最近的波动高点。通过将这些过程与原始的背离检测方法相结合,可以确保只有在存在明显的风险管理和良好的动量一致性的情况下才进行交易。

示例:

int total_bars = Bars(_Symbol,PERIOD_CURRENT,new_rsi_high_time,TimeCurrent());

if(total_bars < 11)
  {
   if(stoch_buffer_k[0] < 80 && stoch_buffer_k[1] > 80)
     {
      //
     }
  }

解释:

当前时间(TimeCurrent())与发现新的 RSI 高点(new_rsi_high_time)的时间之间的柱数由变量 total_bars 决定。如果柱数少于 11 个,它可以保证设置仍然有效并且不会过期。然后,代码确定随机振荡器 (stoch_buffer_k) 的 %K 线是否已在当前柱 (stoch_buffer_k[0] < 80) 中低于超买阈值 (80),并在前一柱 (stoch_buffer_k[1] > 80) 中高于超买阈值。交易设置已验证,如果满足这两个要求,代码将开始运行所需的逻辑以进入交易。

5.2.交易执行

5.2.1.震荡低点和震荡高点

本节将重点介绍确认有效背离和随机振荡信号后执行交易所需的所有基本要素。它确保交易系统地执行,并进行适当的风险管理。

要定义止损(SL),我们必须首先确定最后的波动高点或低点。最后的波动低点将是隐藏看涨背离的止损点,最后的波动高点将是隐藏看跌背离的止损点。这保证了,根据之前的价格行为,交易有一个精确定义的风险阈值。

示例:

double last_high;
double last_low;

//LOGIC FOR LAST HIGH
for(int i = 0; i < 30; i++)
  {
   if(i < 30 && i+1 < 30)
     {
      if(close[i] < open[i] && close[i+1] > open[i+1])
        {

         last_high = MathMax(high[i],high[i+1]);

         break;
        }
     }

  }

//LOGIC FOR LAST LOW
for(int i = 0; i < 30; i++)
  {
   if(i < 30 && i+1 < 30)
     {
      if(close[i] > open[i] && close[i+1] < open[i+1])
        {

         last_low = MathMin(low[i],low[i+1]);

         break;
        }
     }
  }
  }

解释:

为了确定最近的波动高点和低点,此代码会检查前 30 个柱形。它通过检测看跌烛形后跟看涨烛形来确定最高价格(last_high),通过检测看涨烛形后跟看跌烛形来确定最低价格(last_low)。在确定止损水平时,这些数字至关重要。

5.2.2.管理交易执行并防止过度交易

系统每次只允许开立一个仓位,以阻止过度交易并降低不必要的风险。代码将确保在进行新交易之前没有任何未平仓头寸。这通过阻止系统在每个信号上完成交易来保证更好地控制交易行为。有效的交易执行需要包含 #include <Trade/Trade.mqh> 库,该库授予对 CTrade 类的访问权限以进行交易管理。

示例:

#include <Trade/Trade.mqh>
CTrade trade;
input int MagicNumber   = 9097; //MAGIC NUMBER

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {

//SET MAGIC NUMBER
   trade.SetExpertMagicNumber(MagicNumber);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

// Getting total positions
   int totalPositions = 0;

   for(int i = 0; i < PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(ChartID()))
        {
         totalPositions++;
        }
     }
  }

解释:

代码首先包含 Trade.mqh 库,该库提供对 MetaTrader 中交易函数的访问。然后实例化 CTradeobject、trade 来管理交易操作。MagicNumber 被定义为一个输入参数,在本例中设置为 9097,它唯一地标识了该特定 EA 开启的交易,以将它们与同一账户上的其他交易区分开来。在 OnInit() 函数中,使用 SetExpertMagicNumber(MagicNumber) 将幻数应用于交易对象。这可确保 EA 在执行开仓、修改或平仓等操作时识别其交易。

在 OnTick() 函数中,代码通过使用 PositionsTotal() 遍历所有仓位来检查当前图表上交易品种的未平仓仓位总数。它使用 PositionGetTicket(i) 检索每个仓位的编号,并使用 PositionGetInteger(POSITION_MAGIC) 验证仓位的幻数是否与 EA 的幻数匹配。此外,代码使用 PositionGetString(POSITION_SYMBOL) 检查该仓位是否属于当前图表交易品种。如果两个条件都成立,它就会增加 totalPositions 变量,跟踪与特定 EA 和交易品种相关的未平仓交易数量,从而确保每次只开立一个仓位。

5.2.3.风险管理

交易系统通过整合这两个基本要素,保证了风险管理和盈利最大化的系统性方法。该策略支持长期市场成功,并有助于保护交易账户。本节的主要目标是通过使交易员能够表明他们希望在每笔交易中承担的确切金额来实施强有力的风险管理系统。为了确保可能的收益超过可能的风险,它还将描述如何计算每笔交易的风险回报率(RRR)。

示例:

//INPUTS
input double risk_amount = 20; // $ PER TRADE
input double rrr = 4; //RRR
input int MagicNumber   = 9097; //MAGIC NUMBER

double take_profit;
double points_risk;
double lot_size;
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//BULLISH HIDDEN DIVERGENCE
   if(stoch_buffer_k[0] > 20 && stoch_buffer_k[1] < 20 && currentBarTime != lastTradeBarTime && totalPositions < 1)
     {
      take_profit  = ((ask_price - last_low) * rrr) + ask_price;
      points_risk = ask_price - low[0];
      lot_size = CalculateLotSize(_Symbol, risk_amount, points_risk);

      trade.Buy(lot_size,_Symbol,ask_price,last_low,take_profit);
      lastTradeBarTime = currentBarTime;

     }

//BEARISH HIDDEN DIVERGENCE
   if(stoch_buffer_k[0] < 80 && stoch_buffer_k[1] > 80 && currentBarTime != lastTradeBarTime && totalPositions < 1)
     {
      take_profit  = MathAbs(((last_high - ask_price) * rrr) - ask_price);
      points_risk = high[0] - ask_price;
      lot_size = CalculateLotSize(_Symbol, risk_amount, points_risk);

      trade.Sell(lot_size,_Symbol,ask_price,last_high,take_profit);
      lastTradeBarTime = currentBarTime;

     }

  }

//+-----------------------------------------------------------------------+
//| Function to calculate the lot size based on risk amount and stop loss |
//+-----------------------------------------------------------------------+
double CalculateLotSize(string symbol, double riskAmount, double stopLossPips)
  {
// Get symbol information
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
   double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);

// Calculate pip value per lot
   double pipValuePerLot = tickValue / point;

// Calculate the stop loss value in currency
   double stopLossValue = stopLossPips * pipValuePerLot;

// Calculate the lot size
   double lotSize = riskAmount / stopLossValue;

// Round the lot size to the nearest acceptable lot step
   double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
   lotSize = MathFloor(lotSize / lotStep) * lotStep;

// Ensure the lot size is within the allowed range
   double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);

   return lotSize;
  }

解释:

根据给定的风险金额(以美元为单位)和止损距离(以点数为单位),CalculateLotSize 函数计算交易的正确手数。手数大小是通过将风险金额除以止损值、将止损距离转换为货币值以及使用特定交易品种的数据计算每手的点值来确定的。结果经过修改以满足经纪商对最小和最大手数以及手数步长的要求。为了使该函数可以在整个代码中重复使用,并使风险管理统一有效,应该在全局区域中声明它。

对于买入交易,随机震荡指标的 %K 线必须在跌破 20 后再升至 20 以上。仅当没有未平仓头寸(totalPositions < 1)且当前柱形与上次交易的柱形不同时才执行交易。止损设置在最后的波动低点,止盈是使用风险回报率 (rrr) 以及卖价与波动低点之间的距离来计算的。手数大小是使用 CalculateLotSize 函数根据风险金额和止损距离确定的。执行后,lastTradeBarTime 会更新,以防止在同一个柱上重复交易。

图 10. 买入

震荡指标的 %K 线必须首先高于 80 水平(超买),然后才能跌破该水平以指示卖出交易。类似地,只有在没有未平仓头寸且柱形是新的情况下才会执行交易。风险回报率以及卖价与最新波动高点(last_high)之间的差额用于确定止盈(take_profit)。在最后的波动高点处,设置止损。CalculateLotSize 函数再次用于确定手数大小。为了避免重复交易,卖出函数会更新 lastTradeBarTime 并下达卖出订单。

通过遵循交易限额、适当的风险管理和确认信号,该代码保证了有纪律的交易,并提高了交易策略的可靠性。

图 11. 卖出


结论

本文提供了使用 MQL5 识别和交易隐藏的看涨和看跌背离的分步指南,集成了移动平均线、RSI 和随机振荡指标等指标。它强调风险管理、交易执行和自动化,以帮助交易者在市场上有效地发现这些模式。该教育内容旨在教授背离检测和系统交易的基础知识。将附上一份详细的源代码,并附有全面的注释,作为实现所讨论概念的实用参考。

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

最近评论 | 前往讨论 (4)
Oluwatosin Mary Babalola
Oluwatosin Mary Babalola | 6 2月 2025 在 19:12
谢谢
Israel Pelumi Abioye
Israel Pelumi Abioye | 6 2月 2025 在 19:42
Oluwatosin Mary Babalola #:
谢谢
不客气
Jee_Pee
Jee_Pee | 18 2月 2025 在 15:52
你好,伊斯雷尔,谢谢你花了这么多时间在清晰的项目 上,尤其是你的比喻很吸引我,你信守了 "小步快跑 "的承诺。谢谢你,我现在就要开始这个项目了(11),我非常兴奋,我会告诉你我的消息的,约翰。
Israel Pelumi Abioye
Israel Pelumi Abioye | 18 2月 2025 在 17:23
Jee_Pee 项目 上,尤其是你的比喻很吸引我,你信守了 "小步快跑 "的承诺。谢谢你,我现在就要开始这个项目了(11),我很兴奋,我会告诉你我的消息的,约翰。

不客气,谢谢您的美言。

价格行为分析工具包开发(第19部分):ZigZag分析器 价格行为分析工具包开发(第19部分):ZigZag分析器
每一位价格行为交易者都会手动使用趋势线来确认趋势,并找出潜在的转折或延续水平。在这个关于开发价格行为分析工具包的系列中,我们介绍一款专注于绘制倾斜趋势线的工具,以便于进行市场分析。该工具通过清晰地勾勒出有效价格行为评估所必需的关键趋势和水平,简化了交易者的流程。
智能系统健壮性测试 智能系统健壮性测试
在策略开发中,有许多错综复杂的细节需要考虑,对于初学交易者其中许多都未予重视。如是结果,众多交易者,包括我自己,都不得不历经苦难来学习这些教训。本文基于我观察到的大多数初学交易者在 MQL5 上开发策略时常见的陷阱。它将提供一系列提示、技巧、和示例,帮助辨别不合格的 EA,并以一种易于实现的方式来测试我们自己 EA 的稳健性。目标是教导读者,帮助他们未来购买 EA 时避免遭遇骗局,以及预防他们自己开发策略时的错误。
通过配对交易中的均值回归进行统计套利:用数学战胜市场 通过配对交易中的均值回归进行统计套利:用数学战胜市场
本文描述了投资组合层面的统计套利基础知识。其目标是帮助没有深厚数学知识的读者理解统计套利的原则,并提出一个概念性的起点框架。文章包含一个可运行的智能交易系统(EA)、一些关于其一年回测的笔记,以及用于复现实验的相应回测配置设置(.ini 文件)。
精通日志记录(第六部分):数据库日志存储方案 精通日志记录(第六部分):数据库日志存储方案
本文探讨如何利用数据库以结构化、可扩展的方式存储日志。内容涵盖基础概念、核心操作、MQL5中数据库处理器的配置与实现。最后验证结果,并阐述该方法在优化与高效监控方面的优势。