English Русский Deutsch 日本語
preview
价格行为分析工具包开发(第二十六部分):针形线、吞没形态与RSI背离(多模式)工具

价格行为分析工具包开发(第二十六部分):针形线、吞没形态与RSI背离(多模式)工具

MetaTrader 5示例 |
179 0
Christian Benjamin
Christian Benjamin

引言

在我的上一篇文章中,重点识别了四种K线形态:针形线、十字星、吞没形态和光头光脚K线。这些形态一旦识别出来,就会立即用于生成买入或卖出信号。然而,这种方法有时会产生虚假信号,因为信号的确认仅依赖于形态识别,缺乏额外的过滤条件。

本文介绍了一款新工具,专注于两种关键形态:针形线和吞没形态。这些形态与RSI背离相结合,用于确认每个信号。RSI背离是我过去基于价格行为开发的一个独立指标工具。 将形态识别与 RSI 背离相结合,我们构建了一套更稳健、更强大的分析方法。这种全面的价格行为分析不仅能提供更深入的市场洞察,还能显著提高交易的准确率。

我们将首先更深入地探讨这两种形态,然后概述策略、分解代码组件、展示回测结果并进行分析。最后,我们将总结关键要点。请参阅下面的目录。


理解K线形态

K线形态是技术分析中用于研判金融市场行情走势的一种图表工具。它们直观地展示了市场在特定周期内的开盘价、最高价、最低价和收盘价,从而揭示潜在的市场方向和情绪。虽然K线形态种类繁多,但正如本文所述,我们将重点关注两种主要形态:针形线和吞没形态。这些形态在较高时间周期上可能表现为单根或双根K线,而在较低时间周期上则可能由多根K线组成。请看下图示例,我将为您进行详细解析。

第一张图(图1)展示了M30周期上的看跌吞没形态。它由两根K线组成:第一根是实体较小的阳线,随后是一根实体较大的阴线,这根阴线将前一根阳线完全吞没。

图 1. M30 周期看跌吞没形态

如果切换到较短的时间周期,同样的形态可能会由多根K线构成。请看下面的图表(图 2)。它展示了与之前相同的 K 线形态,但现在是在 M5(5分钟)周期上,此时它由多根 K 线组成。

图 2. M5 周期看跌吞没形态

让我们继续讨论重点关注的两种形态:针形线和吞没形态,以及 RSI 背离。我们不会在这里深入探讨这些话题,因为我们在上一篇文章中已经对它们进行了详尽的讲解。您可以通过提供的链接查阅那篇文章。

针形线

针形线是一种 K 线形态,其特征是实体很小,且带有一根显著的长影线,它标志着在特定价位上价格遭到了强烈的抵触。它表现为单根K线,通常预示着市场可能出现反转或处于关键转折点。那根长长的影线表明价格曾在一个方向上急剧移动,但随后被迅速拒绝。这暗示了市场情绪的转变以及动能可能发生改变。

吞没形态

吞没 K 线形态是技术分析中一个流行的工具,它预示着市场方向可能发生反转。它由两根连续的 K 线组成,其中第二根 K 线的实体完全“吞没”了第一根 K 线的实体,表明市场情绪发生了转变。这种形态作为一种反转信号,暗示之前的趋势可能即将结束,而一个新的趋势正在开始。它可以是看涨反转(当一根更大的阳线吞没了前一根较小的阴线时),也可以是看跌反转(当一根更大的阴线吞没了前一根较小的阳线时)。

RSI 背离

RSI 背离是指资产价格与 RSI 指标运行方向出现背道而驰的现象,这预示着动能可能正在发生变化,趋势也存在反转的风险。当价格形成新的高点或低点,而 RSI 却未能确认形成相应的高点或低点时,这种背离就显现出来了。主要有两种类型:当价格创出新高,而 RSI 却未能突破前高(形成顶背离)时出现。这表明向上的动能正在减弱,价格可能即将下跌。看涨背离—当价格创出新低,而 RSI 却形成一个较高的低点时发生。这表明向下的动能正在减弱,随后可能迎来上涨。

RSI 作为一种动量震荡指标,用于衡量价格变动的速度和幅度,帮助交易者识别超买或超卖状况,但它也能揭示背离形态。当价格触及新的极值,而 RSI 却并未同步跟进时,这就发出了一个明确的信号:动能正在悄然发生转变

看跌背离

图 3. 看跌 RSI 背离

看涨 RSI 背离

图 4. 看涨 RSI 背离


策略概述

本程序会全面扫描并精准识别针形线与吞没形态,同时检查 RSI 背离,以此作为确认每个买卖信号的必要条件。换句话说,当检测到针形线或吞没形态时,EA 会验证 RSI 背离以确认信号的有效性。只有当 RSI 确认了形态后,程序才会显示交易信号。下图展示了针形线如何通过看涨 RSI 背离得到确认。

图 5. 针形线的确认


代码段详解

1. 基本数据与初始化属性

EA 的开头部分定义了关键的基本数据,这些数据用于在 MetaTrader 中识别和管理脚本。版权申明、链接和版本号提供了归属信息和版本控制,确保用户了解脚本的来源和更新状态。 #property strict 指令强制执行更严格的编译规则,能及早发现潜在错误并促进良好的编码习惯。这种配置确保 EA 符合既定标准,在多个脚本同时运行时,也便于后期的维护与排错。

//+------------------------------------------------------------------+
//|                                           Multi-Pattern Signal EA|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/en/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

2. 引入类库和定义输入参数

EA 引入了<Trade/Trade.mqh>库,以启用订单操作,例如开仓、平仓和管理交易。虽然此版本主要侧重于形态检测和警报,但集成交易库为未来扩展自动交易能力奠定基础。 输入参数对于自定义至关重要:交易者可以调整 RSI 周期以改变灵敏度,设置超买和超卖阈值以检测背离,并以点数为单位配置止损和止盈水平。

EntryBuffer作为入场缓冲带,可避免在距离最新价格极值过近的位置入场,从而减少虚假信号。通知标志用于启用或禁用声音警报、推送通知和电子邮件,让交易者能够控制接收警报的方式。这些参数共同使 EA 能够适应各种交易风格和市场条件。

#include <Trade/Trade.mqh>

input int    RSI_Period     = 14;      // RSI calculation period
input double RSI_Overbought = 70.0;    // RSI level indicating overbought
input double RSI_Oversold   = 30.0;    // RSI level indicating oversold
input double SL_Pips        = 20.0;    // Default stop loss in pips
input double TP_Pips        = 20.0;    // Default take profit in pips
input double EntryBuffer    = 5.0;     // Buffer in points for entry price
input bool   EnableSound    = true;    // Enable sound alerts
input bool   EnablePush     = true;    // Enable push notifications
input bool   EnableEmail    = false;   // Enable email alerts

3. 初始化:创建指标句柄与缓冲区

在初始设置期间,EA 使用 iRSI() 创建一个 RSI 指标句柄,指定交易品种、周期和 RSI 参数。该句柄对于在每次报价期间检索实时 RSI 值至关重要,它是检测背离的基础。数组被设置为“序列(Series)”模式,这意味着最新数据始终位于索引 0 处。初始化时间、开盘价、最高价、最低价和收盘价的内部缓冲区,以存储用于分析的历史数据。lastBarTime 变量跟踪最后处理的K线的时间戳,防止在同一根K线上生成多个信号,从而避免冗余警报并确保信号的准确性。

int OnInit()
{
    rsiHandle = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE);
    if(rsiHandle == INVALID_HANDLE)
        return INIT_FAILED;

    // Set arrays as series for easier access to recent data
    ArraySetAsSeries(rsiBuffer,   true);
    ArraySetAsSeries(timeBuffer,  true);
    ArraySetAsSeries(openBuffer,  true);
    ArraySetAsSeries(highBuffer,  true);
    ArraySetAsSeries(lowBuffer,   true);
    ArraySetAsSeries(closeBuffer, true);

    return INIT_SUCCEEDED;
}

4. 在 OnTick() 中进行连续数据监控与处理

OnTick()函数在每次收到行情报价时触发,充当 EA 的核心循环引擎。它首先验证是否有至少 20 根 K线可用,以确保有足够的历史数据进行形态识别。然后,它将最新的 20 个数据点(时间戳和开盘、高、低、收盘价)复制到内部缓冲区中。这一步至关重要,因为市场数据在不断更新,分析依赖于最新且准确的数据。如果任何复制操作失败,该函数将中止以防止错误或虚假信号。

为了防止在同一根 K 线内生成多个信号,它会检查上一根 K 线的时间戳是否与lastBarTime 匹配。 如果一致,那么无需处理;否则用新K线的时间戳更新lastBarTime。然后会刷新RSI值。数据准备就绪后,它调用 FindSignalBar() 来分析最近的 K 线是否存在背离形态和 K 线信号,这是生成告警的基础。

void OnTick()
{
    // Ensure enough data is available
    if(Bars(_Symbol, _Period) < 20)
        return;

    // Copy recent market data
    if(CopyTime(_Symbol, _Period, 0, 20, timeBuffer)   <= 0 ||
       CopyOpen(_Symbol, _Period, 0, 20, openBuffer)   <= 0 ||
       CopyHigh(_Symbol, _Period, 0, 20, highBuffer)   <= 0 ||
       CopyLow(_Symbol, _Period, 0, 20, lowBuffer)     <= 0 ||
       CopyClose(_Symbol, _Period, 0, 20, closeBuffer) <= 0)
       return;

    // Prevent multiple signals in the same candle
    if(timeBuffer[1] == lastBarTime)
        return;
    lastBarTime = timeBuffer[1];

    // Update RSI data
    if(CopyBuffer(rsiHandle, 0, 0, 20, rsiBuffer) <= 0)
        return;

    // Detect potential divergence and pattern
    int dir = FindSignalBar();
    if(dir == 0)
        return;

    // Determine direction and compute entry/stop levels
    bool isBullish = (dir > 0);
    int idx = 1; // most recent completed bar
    double entry = isBullish
                   ? highBuffer[idx] + EntryBuffer * _Point
                   : lowBuffer[idx] - EntryBuffer * _Point;
    double stopL = isBullish
                   ? lowBuffer[idx] - SL_Pips * _Point
                   : highBuffer[idx] + SL_Pips * _Point;

    // Visualize and notify
    DrawSignal(idx, isBullish, entry, stopL);
}

5. 基于背离与K线形态的形态识别

FindSignalBar() 函数在识别潜在反转信号中起着关键作用。它扫描最近的K线(回溯5至15根),以检测背离形态。当价格低点逐步抬高,而RSI指标跌至超卖区域时,即确认看涨背离,预示价格可能向上反转。相反,当价格高点逐步降低,而RSI处于超买区域时,则构成看跌背离,暗示价格可能向下回调。

该函数还会检查最新一根K线是否出现具有反转意义的经典形态,如针形线或吞没形态。只有当背离条件与K线形态同时满足时,函数才返回+1(看涨信号)或-1(看跌信号);否则返回0,表示当前无有效交易信号。这种结合动量背离与价格行为的分层分析方法,有效提升了信号的可靠性。

int FindSignalBar()
{
    bool bullDiv = false, bearDiv = false;
    for(int i = 5; i <= 15; i++)
    {
        // Bullish divergence condition
        if(lowBuffer[i] > lowBuffer[1] && rsiBuffer[i] < rsiBuffer[1] && rsiBuffer[1] < RSI_Oversold)
            bullDiv = true;
        // Bearish divergence condition
        if(highBuffer[i] < highBuffer[1] && rsiBuffer[i] > rsiBuffer[1] && rsiBuffer[1] > RSI_Overbought)
            bearDiv = true;
    }

    // No divergence detected
    if(!bullDiv && !bearDiv)
        return 0;

    // Check for candlestick patterns supporting divergence
    bool bullPat = IsBullishPinBar(1) || IsBullishEngulfing(1);
    bool bearPat = IsBearishPinBar(1) || IsBearishEngulfing(1);

    // Confirmed signals
    if(bullDiv && bullPat)
        return +1;
    if(bearDiv && bearPat)
        return -1;

    return 0; // No valid signal
}

6. 用于确认信号的K线形态检测

这些函数通过分析单根K线来确认反转信号。IsBullishPinBar() 用于判断K线是否具有小实体和长下影线,表明价格曾下探但被强势拉回,显示出对低位的拒绝,是潜在的看涨反转信号。IsBearishPinBar() 则相反。IsBullishEngulfing()判断当前K线实体是否完全吞没前一根K线的实体,若成立,则表明买方力量强劲,可能开启上涨;而IsBearishEngulfing()则识别卖方主导的吞没形态,预示下跌压力增强。这些形态检测函数通过计算K线实体和影线相对于整个高低点范围的占比,确保只识别结构清晰、形态标准的K线组合,从而有效减少误判,提升信号的可靠性。

// Bullish Pin Bar Pattern
bool IsBullishPinBar(int i)
{
    double body = MathAbs(openBuffer[i] - closeBuffer[i]);
    double rng  = highBuffer[i] - lowBuffer[i];
    double lw   = MathMin(openBuffer[i], closeBuffer[i]) - lowBuffer[i];
    double uw   = highBuffer[i] - MathMax(openBuffer[i], closeBuffer[i]);
    return closeBuffer[i] > openBuffer[i]
           && lw > 2.0 * body
           && uw < 0.5 * body
           && body > 0.1 * rng;
}

// Bearish Pin Bar Pattern
bool IsBearishPinBar(int i)
{
    double body = MathAbs(openBuffer[i] - closeBuffer[i]);
    double rng  = highBuffer[i] - lowBuffer[i];
    double uw   = highBuffer[i] - MathMax(openBuffer[i], closeBuffer[i]);
    double lw   = MathMin(openBuffer[i], closeBuffer[i]) - lowBuffer[i];
    return closeBuffer[i] < openBuffer[i]
           && uw > 2.0 * body
           && lw < 0.5 * body
           && body > 0.1 * rng;
}

// Bullish Engulfing Pattern
bool IsBullishEngulfing(int i)
{
    if(closeBuffer[i] <= openBuffer[i])
        return false;
    if(openBuffer[i] > closeBuffer[i+1])
        return false;
    if(closeBuffer[i] < openBuffer[i+1])
        return false;
    return true;
}

// Bearish Engulfing Pattern
bool IsBearishEngulfing(int i)
{
    if(closeBuffer[i] >= openBuffer[i])
        return false;
    if(openBuffer[i] < closeBuffer[i+1])
        return false;
    if(closeBuffer[i] > openBuffer[i+1])
        return false;
    return true;
}

7. 可视化与生成告警信号

一旦确认了有效的形态和背离,DrawSignal()函数便会执行绘图任务。它会在入场点绘制箭头,标记入场和止损位,并画出趋势线以直观展示背离形态。箭头的颜色和代码对应交易方向:看涨信号显示为绿色系,看跌信号则为红色。趋势线连接近期的低点或高点,辅助验证背离走势。

该函数还会生成详细的警报信息,内容涵盖形态类型、方向、交易品种、时间、入场价及止损位。随后,系统会根据用户的偏好设置触发通知——无论是播放提示音、推送消息还是发送邮件——确保交易者能第一时间获知信号并迅速做出反应。这套完善的可视化与警报系统,有效提升了交易者的市场感知力和决策效率。

void DrawSignal(int i, bool isBullish, double entry, double stopL)
{
    string tag = TimeToString(timeBuffer[i], TIME_SECONDS);
    string nameA = "Arr_" + tag;
    string nameE = "Ent_" + tag;
    string nameS = "SL_" + tag;
    string nameL = "Div_" + tag;

    color clrArr = isBullish ? clrLime : clrRed;
    int code = isBullish ? 233 : 234;

    // Arrow at entry point
    ObjectCreate(0, nameA, OBJ_ARROW, 0, timeBuffer[i], entry);
    ObjectSetInteger(0, nameA, OBJPROP_COLOR, clrArr);
    ObjectSetInteger(0, nameA, OBJPROP_ARROWCODE, code);
    ObjectSetInteger(0, nameA, OBJPROP_WIDTH, 2);

    // Horizontal lines for entry and stop-loss
    ObjectCreate(0, nameE, OBJ_HLINE, 0, 0, entry);
    ObjectSetInteger(0, nameE, OBJPROP_COLOR, clrAqua);
    ObjectCreate(0, nameS, OBJ_HLINE, 0, 0, stopL);
    ObjectSetInteger(0, nameS, OBJPROP_COLOR, clrOrangeRed);
    ObjectSetInteger(0, nameS, OBJPROP_STYLE, STYLE_DASH);

    // Divergence trend line for visual confirmation
    for(int j = i + 5; j < i + 15; j++)
    {
        if(isBullish && lowBuffer[j] > lowBuffer[i])
        {
            ObjectCreate(0, nameL, OBJ_TREND, 0,
                         timeBuffer[j], lowBuffer[j],
                         timeBuffer[i], lowBuffer[i]);
            ObjectSetInteger(0, nameL, OBJPROP_COLOR, clrDodgerBlue);
            break;
        }
        if(!isBullish && highBuffer[j] < highBuffer[i])
        {
            ObjectCreate(0, nameL, OBJ_TREND, 0,
                         timeBuffer[j], highBuffer[j],
                         timeBuffer[i], highBuffer[i]);
            ObjectSetInteger(0, nameL, OBJPROP_COLOR, clrOrange);
            break;
        }
    }

    // Construct alert message
    string pattern = isBullish
                     ? (IsBullishEngulfing(i) ? "Engulfing" : "PinBar")
                     : (IsBearishEngulfing(i) ? "Engulfing" : "PinBar");
    string side = isBullish ? "Buy" : "Sell";
    string txt = StringFormat(
                     "%s + RSI Divergence %s Signal\nSymbol: %s\nTime: %s\nEntry: %.5f\nSL: %.5f",
                     pattern, side, _Symbol,
                     TimeToString(timeBuffer[i], TIME_MINUTES),
                     entry, stopL
                 );

    // Notify trader
    Alert(txt);
    if(EnableSound)
        PlaySound("alert.wav");
    if(EnablePush)
        SendNotification(txt);
    if(EnableEmail)
        SendMail("Signal EA Alert", txt);

    Print(txt);
}

完整的MQL5 EA如下:

//+------------------------------------------------------------------+
//|                                           Multi-Pattern Signal EA|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/en/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

#include <Trade/Trade.mqh>

input int    RSI_Period     = 14;
input double RSI_Overbought = 70.0;
input double RSI_Oversold   = 30.0;
input double SL_Pips        = 20.0;  // in pips
input double TP_Pips        = 20.0;  // in pips
input double EntryBuffer    = 5.0;   // in points
input bool   EnableSound    = true;
input bool   EnablePush     = true;
input bool   EnableEmail    = false;

CTrade   trade;

// internal buffers
double   rsiBuffer[];
datetime timeBuffer[];
double   openBuffer[], highBuffer[], lowBuffer[], closeBuffer[];
int      rsiHandle;
datetime lastBarTime = 0;

//+------------------------------------------------------------------+
int OnInit()
  {
   rsiHandle = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE);
   if(rsiHandle == INVALID_HANDLE)
      return INIT_FAILED;

   ArraySetAsSeries(rsiBuffer,   true);
   ArraySetAsSeries(timeBuffer,  true);
   ArraySetAsSeries(openBuffer,  true);
   ArraySetAsSeries(highBuffer,  true);
   ArraySetAsSeries(lowBuffer,   true);
   ArraySetAsSeries(closeBuffer, true);

   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
void OnTick()
  {
   if(Bars(_Symbol,_Period) < 20)
      return;

   if(CopyTime(_Symbol,_Period,0,20,timeBuffer)   <= 0 ||
      CopyOpen(_Symbol,_Period,0,20,openBuffer)   <= 0 ||
      CopyHigh(_Symbol,_Period,0,20,highBuffer)   <= 0 ||
      CopyLow(_Symbol,_Period,0,20,lowBuffer)     <= 0 ||
      CopyClose(_Symbol,_Period,0,20,closeBuffer) <= 0)
      return;

   if(timeBuffer[1] == lastBarTime)
      return;
   lastBarTime = timeBuffer[1];

   if(CopyBuffer(rsiHandle,0,0,20,rsiBuffer) <= 0)
      return;

   int dir = FindSignalBar();
   if(dir == 0)
      return;

   int idx = 1;
   bool isBullish = (dir > 0);

   double entry = isBullish
                  ? highBuffer[idx] + EntryBuffer * _Point
                  : lowBuffer[idx]  - EntryBuffer * _Point;
   double stopL = isBullish
                  ? lowBuffer[idx]  - SL_Pips * _Point
                  : highBuffer[idx] + SL_Pips * _Point;

   DrawSignal(idx, isBullish, entry, stopL);
  }

//+------------------------------------------------------------------+
int FindSignalBar()
  {
   bool bullDiv = false, bearDiv = false;
   for(int i = 5; i <= 15; i++)
     {
      if(lowBuffer[i] > lowBuffer[1] && rsiBuffer[i] < rsiBuffer[1] && rsiBuffer[1] < RSI_Oversold)
         bullDiv = true;
      if(highBuffer[i] < highBuffer[1] && rsiBuffer[i] > rsiBuffer[1] && rsiBuffer[1] > RSI_Overbought)
         bearDiv = true;
     }
   if(!bullDiv && !bearDiv)
      return 0;

   bool bullPat = IsBullishPinBar(1) || IsBullishEngulfing(1);
   bool bearPat = IsBearishPinBar(1) || IsBearishEngulfing(1);

   if(bullDiv && bullPat)
      return +1;
   if(bearDiv && bearPat)
      return -1;
   return 0;
  }

//+------------------------------------------------------------------+
bool IsBullishPinBar(int i)
  {
   double body = MathAbs(openBuffer[i] - closeBuffer[i]);
   double rng  = highBuffer[i] - lowBuffer[i];
   double lw   = MathMin(openBuffer[i], closeBuffer[i]) - lowBuffer[i];
   double uw   = highBuffer[i] - MathMax(openBuffer[i], closeBuffer[i]);
   return closeBuffer[i] > openBuffer[i]
          && lw > 2.0 * body
          && uw < 0.5 * body
          && body > 0.1 * rng;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsBearishPinBar(int i)
  {
   double body = MathAbs(openBuffer[i] - closeBuffer[i]);
   double rng  = highBuffer[i] - lowBuffer[i];
   double uw   = highBuffer[i] - MathMax(openBuffer[i], closeBuffer[i]);
   double lw   = MathMin(openBuffer[i], closeBuffer[i]) - lowBuffer[i];
   return closeBuffer[i] < openBuffer[i]
          && uw > 2.0 * body
          && lw < 0.5 * body
          && body > 0.1 * rng;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsBullishEngulfing(int i)
  {
   if(closeBuffer[i] <= openBuffer[i])
      return false;
   if(openBuffer[i]  > closeBuffer[i+1])
      return false;
   if(closeBuffer[i] < openBuffer[i+1])
      return false;
   return true;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsBearishEngulfing(int i)
  {
   if(closeBuffer[i] >= openBuffer[i])
      return false;
   if(openBuffer[i]  < closeBuffer[i+1])
      return false;
   if(closeBuffer[i] > openBuffer[i+1])
      return false;
   return true;
  }

//+------------------------------------------------------------------+
void DrawSignal(int i, bool isBullish, double entry, double stopL)
  {
   string tag   = TimeToString(timeBuffer[i], TIME_SECONDS);
   string nameA = "Arr_" + tag;
   string nameE = "Ent_" + tag;
   string nameS = "SL_"  + tag;
   string nameL = "Div_" + tag;

   color clrArr = isBullish ? clrLime : clrRed;
   int   code   = isBullish ? 233 : 234;

   ObjectCreate(0, nameA, OBJ_ARROW, 0, timeBuffer[i], entry);
   ObjectSetInteger(0, nameA, OBJPROP_COLOR, clrArr);
   ObjectSetInteger(0, nameA, OBJPROP_ARROWCODE, code);
   ObjectSetInteger(0, nameA, OBJPROP_WIDTH, 2);

   ObjectCreate(0, nameE, OBJ_HLINE, 0, 0, entry);
   ObjectSetInteger(0, nameE, OBJPROP_COLOR, clrAqua);
   ObjectCreate(0, nameS, OBJ_HLINE, 0, 0, stopL);
   ObjectSetInteger(0, nameS, OBJPROP_COLOR, clrOrangeRed);
   ObjectSetInteger(0, nameS, OBJPROP_STYLE, STYLE_DASH);

   for(int j = i + 5; j < i + 15; j++)
     {
      if(isBullish && lowBuffer[j] > lowBuffer[i])
        {
         ObjectCreate(0, nameL, OBJ_TREND, 0,
                      timeBuffer[j], lowBuffer[j],
                      timeBuffer[i], lowBuffer[i]);
         ObjectSetInteger(0, nameL, OBJPROP_COLOR, clrDodgerBlue);
         break;
        }
      if(!isBullish && highBuffer[j] < highBuffer[i])
        {
         ObjectCreate(0, nameL, OBJ_TREND, 0,
                      timeBuffer[j], highBuffer[j],
                      timeBuffer[i], highBuffer[i]);
         ObjectSetInteger(0, nameL, OBJPROP_COLOR, clrOrange);
         break;
        }
     }

   string pattern = isBullish
                    ? (IsBullishEngulfing(i) ? "Engulfing" : "PinBar")
                    : (IsBearishEngulfing(i) ? "Engulfing" : "PinBar");
   string side    = isBullish ? "Buy" : "Sell";
   string txt     = StringFormat(
                       "%s + RSI Divergence %s Signal\nSymbol: %s\nTime: %s\nEntry: %.5f\nSL: %.5f",
                       pattern, side, _Symbol,
                       TimeToString(timeBuffer[i], TIME_MINUTES),
                       entry, stopL
                    );

   Alert(txt);
   if(EnableSound)
      PlaySound("alert.wav");
   if(EnablePush)
      SendNotification(txt);
   if(EnableEmail)
      SendMail("Signal EA Alert", txt);
   Print(txt);
  }
//+------------------------------------------------------------------+


回测与结果

回测是通过历史市场数据来评估交易策略的过程,用以估算策略在过去的表现。这项模拟能让交易者看清预设的交易规则在真实市场环境下会如何运作,从而洞察其潜在的盈利能力与风险。通过分析这些模拟交易的结果,交易者可以决定是否将该策略投入实盘交易。

第一步是明确界定交易策略。这需要建立具体的进出场规则,包括触发买卖信号的指标、价格形态或其他条件。清晰的策略定义能确保测试的一致性与结果的准确性。接下来,交易者需要收集相关的历史数据,例如价格图表、成交量及其他分析所需的信息。准确且全面的数据是获得可靠回测结果的基础。

选择合适的回测平台至关重要。市面上有多种软件工具可供选择,有的支持手动分析,有的支持全自动测试。所选平台必须能根据你的策略规则准确模拟交易,并提供详尽的绩效指标。选定平台后,需要搭建回测环境,包括配置时间周期、交易成本、滑点等参数。这些因素都会影响绩效,因此设置时应尽可能贴近真实的交易环境。

环境配置完成后,即可运行回测。平台会根据预设规则与历史数据自动生成模拟交易记录。在此过程中,深入分析结果是关键。总盈亏、胜率、最大回撤以及盈亏比等核心指标,能提供关于策略有效性与稳健性的宝贵信息。

基于分析结果,交易者应优化并微调策略。调整参数或规则可以改善表现,并解决测试中发现的弱点。为了确保策略的可靠性并避免过度拟合,必须使用样本外数据进行验证。在一组独立的历史数据上运行优化后的策略,有助于确认其稳健性及其在实盘市场中的潜力。

以下是本次回测的结果。

图 6. 看涨吞没形态确认

上图(图6)截取自 EURUSD 在 M30 周期的回测画面,展示了一个由看涨背离确认的看涨吞没形态。

图 7. USDCHF 回测演示

上图(图7)截取自 EURUSD 在 M30 周期的回测画面,展示了一个由看涨背离确认的看涨吞没形态。此外,下方的动图演示了 USDCHF 货币对的回测过程,展示了系统如何识别出多个信号。


结论

该EA展示了一种在策略中进行形态识别与背离检测的进阶方法。通过将 RSI 等技术指标与针形线、吞没形态等K线分析相结合,它能有效识别出高概率的反转信号。该EA采用模块化设计,能够实时分析近期市场数据,在图表上生成可视化信号,并发送及时的警报,这是交易者追求系统化、规范化入场的得力工具。 

回测结果表明,当信号得到多重条件确认时,该EA能实现极高的胜率。然而,如同任何自动化策略一样,通过样本外测试在不同货币对和市场环境下验证其稳健性至关重要。合理的参数优化与风险管理是最大化盈利并控制回撤的关键。总体而言,该EA展示了将形态识别与背离分析相结合的潜力,为交易系统进一步整合机器学习与智能决策奠定了坚实基础。


日期 工具名称  说明 版本  更新  提示
01/10/24 图表投影仪 用于叠加前一天价格走势(带“幽灵”效果)的脚本。 1.0 首次发布 工具1
18/11/24 分析评论 它以表格形式提供前一日的市场信息,并预测市场的未来走向。 1.0 首次发布 工具2
27/11/24 分析大师 市场指标每两小时定期更新  1.01 第二版 工具3
02/12/24 分析预测器  每两小时定期更新市场指标,并集成Telegram推送功能。 1.1 第三版 工具4
09/12/24 波动性导航工具 该EA使用布林带、RSI和ATR指标分析市场状况。 1.0 首次发布 工具5
19/12/24 均值回归信号收割器  使用均值回归策略分析市场并提供信号  1.0  首次发布  工具6 
9/01/25  信号脉冲  多时间框架分析器 1.0  首次发布  工具7 
17/01/25  指标看板  带按钮的分析面板  1.0  首次发布 工具8 
21/01/25 外部资金流 通过外部库进行分析 1.0  首次发布 工具9 
27/01/25 VWAP 成交量加权平均价格   1.3  首次发布  工具10 
02/02/25  Heikin Ashi  趋势平滑与反转信号识别  1.0  首次发布  工具11
04/02/25  FibVWAP  通过 Python 分析生成信号  1.0  首次发布  工具12
14/02/25  RSI 背离  价格走势与 RSI 背离的对比  1.0  首次发布  工具13 
17/02/25  抛物线转向与反转 (PSAR)  自动化PSAR策略 1.0 首次发布  工具14
20/02/25  四分位绘制脚本  在图表上绘制四分位水平线  1.0  首次发布  工具15 
27/02/25  侵入检测器 当价格触及四分位水平时进行检测和警报 1.0   首次发布 工具16 
27/02/25  TrendLoom工具 多时间周期分析面板 1.0 首次发布 工具17
11/03/25  四分位看板  带有可激活或禁用四分位的面板  1.0  首次发布 工具18
26/03/25  ZigZag 分析器  使用ZigZag指标绘制趋势线  1.0  首次发布  工具19 
10/04/25  相关性检测器 使用Python库绘制货币对的相关性。 1.0 首次发布  工具20 
23/04/25 市场结构反转检测工具 市场结构反转检测 1.0  首次发布  工具21
08/05/25  相关性仪表盘  不同货币对之间的相关性 1.0 首次发布 工具22 
13/05/25 货币强弱指标  衡量货币对中每种货币的强弱 1.0 首次发布 工具23 
21/05/25 PAQ 分析工具  K线形态检测器 1.0 首次发布 工具24 
23/05/25 长影线,吞没形态和RSI背离 利用RSI背离确认针形线与吞没形态信号  1.0 首次发布 工具25 

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

交易中的神经网络:二维连接空间模型(终篇) 交易中的神经网络:二维连接空间模型(终篇)
我们继续探索创新的奇美拉(Chimera)框架 — 这款二维状态空间模型,利用神经网络技术多维度分析时间序列。该方法提供了高预测精度、及低计算成本。
价格行为分析工具包开发(第二十五部分):双指数移动平均线(EMA)分形突破策略 价格行为分析工具包开发(第二十五部分):双指数移动平均线(EMA)分形突破策略
价格行为分析是识别盈利交易机会的基础方法。然而,人工监测价格走势和形态不仅困难而且极其耗时。为解决这一痛点,我们开发了自动分析价格行为的工具,一旦检测到潜在机会,就会立刻发出信号。本文将介绍一款强大的工具,该工具结合分形突破以及14周期指数移动平均线(EMA 14)和200周期指数移动平均线(EMA 200)来生成可靠的交易信号,帮助交易者更自信地做出明智决策。
价格行为分析工具包开发(第二十七部分):利用移动平均线进行流动性扫单 价格行为分析工具包开发(第二十七部分):利用移动平均线进行流动性扫单
理解价格走势背后的微妙动态,能让您获得至关重要的优势。流动性扫单便是这样一种现象,大型交易者(尤其是机构)会刻意运用这一策略,推动价格突破关键支撑位或阻力位。这些价位往往集中了零售交易者的止损单,从而形成流动性池,大资金玩家可以借此机会买入或卖出大额头寸,且滑点极小。
风险管理(第四部分):完善关键类方法 风险管理(第四部分):完善关键类方法
这是我们关于 MQL5 风险管理系列文章的第四部分,我们将继续探索保护和优化交易策略的高级方法。在前几篇文章中奠定了重要的基础之后,我们现在将专注于完成第三部分中推迟的所有剩余方法,包括检查是否达到特定利润或亏损水平的函数。此外,我们将引入新的关键事件,以实现更准确、更灵活的风险管理。