English Русский Español Deutsch 日本語 Português
preview
如何将聪明资金概念(SMC)与 RSI 指标结合到 EA 中

如何将聪明资金概念(SMC)与 RSI 指标结合到 EA 中

MetaTrader 5示例 | 4 二月 2025, 10:10
519 0
Hlomohang John Borotho
Hlomohang John Borotho

概述

在快节奏的外汇交易世界中,拥有一个可靠高效的交易系统对于成功至关重要。一般来说,交易有很多术语、概念和策略。有时,这往往会让人喘不过气来,尤其是对于那些仍在努力在交易行业站稳脚跟的新交易员来说。  聪明资金概念(SMC,Smart Money Concept)是外汇交易中的热门概念之一,但对于新交易者或任何人来说,在交易中使用聪明资金概念有时都很困难。

为了解决这个问题,应该有一个强大的工具,根据市场结构和价格走势自动做出交易决策。解决方案是将聪明资金概念(结构突破)与流行的相对强弱指数(RSI)指标相结合。这种组合通过利用价格行为和动量分析提供了战略优势,提高了交易进场和退场的准确性,旨在优化交易表现。


EA 交易示例的思路

这个 EA 交易示例的思路和功能是,EA 将检测波段低点和波段高点,因为一些聪明资金概念会利用波段。RSI指标仍将使用传统的方式,即 70 水平用于超买市场,30 水平用于超卖市场,周期数为 8。当市场价格高于之前检测到的高点时,这将表明结构向上突破。同样,当市场价格低于之前检测到的低点时,这将表明下行结构的突破。


现在,让我们开发 EA 交易示例

 该 EA 的目标是根据市场条件和 RSI 水平开启买入和卖出订单。具体来说,它:

  1. 识别市场中的波段高点和波段低点。
  2. 检查市场价格是否高于前一个波段高点(卖出信号)或低于前一个波段低点(买入信号)。
  3. 然后用 RSI 水平确认信号。

因此,基本上 EA 交易会搜索结构的突破或之前波动(高/低)的突破,然后如果 RSI 值在指定的设置范围内,则会执行市场订单(买入/卖出)。


代码分解

1.属性和包含文件

#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Trade/Trade.mqh>

这些代码行定义了 EA 的属性,并且包含执行交易所需的交易库。

2.全局变量和输入参数

long MagicNumber = 76543;
double lotsize = 0.01;
input int RSIperiod = 8;
input int RSIlevels = 70;
int stopLoss = 200;
int takeProfit = 500;
bool closeSignal = false;
  • MagicNumber:EA 交易的唯一标识符。
  • Lotsize:每笔交易的手数大小。
  • RSIperiod:用于计算 RSI 的周期数。
  • RSIlevels:RSI 临界值水平。
  • stopLoss 和 takeProfit:以点数为单位的止损和止盈水平。
  • CloseSignal:根据与当前打开仓位相反的信号标记关闭仓位。

3.RSI 变量

int handle;
double buffer[];
MqlTick currentTick;
CTrade trade;
datetime openTimeBuy = 0;
datetime openTimeSell = 0;

  • Handle:RSI 指标的句柄。
  • Buffer:用于存储 RSI 值的数组。
  • currentTick:存储当前市场价格的结构。
  • Trade:这是交易操作的对象。
  • openTimeBuy 和 openTimeSell:最后买卖信号和操作的时间戳。

4.初始化函数

int OnInit() {
    if (RSIperiod <= 1) {
        Alert("RSI period <= 1");
        return INIT_PARAMETERS_INCORRECT;
    }
    if (RSIlevels >= 100 || RSIlevels <= 50) {
        Alert("RSI level >= 100 or <= 50");
        return INIT_PARAMETERS_INCORRECT;
    }
    trade.SetExpertMagicNumber(MagicNumber);
    handle = iRSI(_Symbol, PERIOD_CURRENT, RSIperiod, PRICE_CLOSE);
    if (handle == INVALID_HANDLE) {
        Alert("Failed to create indicator handle");
        return INIT_FAILED;
    }
    ArraySetAsSeries(buffer, true);
    return INIT_SUCCEEDED;
}
  • 验证 RSI 周期和水平。
  • 设置交易识别的幻数。
  • 创建 RSI 指标句柄。
  • 将缓冲区设置为按时间倒序存储 RSI 值的序列。

5.去初始化函数

void OnDeinit(const int reason) {
    if (handle != INVALID_HANDLE) {
        IndicatorRelease(handle);
    }
}

删除 EA 时释放 RSI 指标句柄。

6.OnTick 函数

众所周知,OnTick 函数包含检测信号和执行交易的核心逻辑。

void OnTick() {
    static bool isNewBar = false;
    int newbar = iBars(_Symbol, _Period);
    static int prevBars = newbar;
    if (prevBars == newbar) {
        isNewBar = false;
    } else if (prevBars != newbar) {
        isNewBar = true;
        prevBars = newbar;
    }


首先,由于 OnTick 函数在每个分时报价上运行,我们需要确保在检测信号或执行交易时,每个柱形都执行一次。为此,我们声明了一个静态布尔变量 isNewBar我们首先将其设置为 false,然后声明一个 int 型变量 newBar,并将其赋值给函数 iBars,这样我们就可以跟踪每个蜡烛柱。

  • static bool isNewBar:跟踪是否形成了新的柱形(烛形)。
  • int newbar = iBars(_Symbol, _Period):获取图表上的当前柱形数。
  • static int prevBars = newbar:初始化前一个柱形计数。
  • if-else 块检查柱形计数是否发生变化,表明出现了新的柱形。如果形成了新的柱形,"isNewBar" 就会设置为 "true",否则就是 "false"。
    const int length = 10;
    int right_index, left_index;
    int curr_bar = length;
    bool isSwingHigh = true, isSwingLow = true;
    static double swing_H = -1.0, swing_L = -1.0;

然后,我们需要设置变量来检测波段高点和波段低点:

  • const int length = 10:定义检测波段高点和波段低点的范围。
  • int right_index, left_index:当前柱形右侧和左侧柱形的索引。
  • int curr_bar = length:设置当前柱形索引。
  • bool isSwingHigh = true, isSwingLow = true:这些标志可用于确定柱形是波段高点还是波段低点。
  • static double swing_H = -1.0, swing_L = -1.0:存储最新检测到的波段最高值和波段最低值。
    double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
    double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);

变量类型为双精度型变量,变量 "Ask" 用于获取当前市场的卖出价格,变量 "Bid" 用于获取当前市场的买入价格:

  • double Ask:获取当前卖出价格
  • double Bid:获取当前买入价格
  • NormalizeDouble:将价格四舍五入到正确的小数位数。
    if (isNewBar) {
        for (int a = 1; a <= length; a++) {
            right_index = curr_bar - a;
            left_index = curr_bar + a;

            if ((high(curr_bar) <= high(right_index)) || (high(curr_bar) < high(left_index))) {
                isSwingHigh = false;
            }
            if ((low(curr_bar) >= low(right_index)) || (low(curr_bar) > low(left_index))) {
                isSwingLow = false;
            }
        }

        if (isSwingHigh) {
            swing_H = high(curr_bar);
            Print("We do have a swing high at: ", curr_bar, " H: ", high(curr_bar));
            drawswing(TimeToString(time(curr_bar)), time(curr_bar), high(curr_bar), 32, clrBlue, -1);
        }
        if (isSwingLow) {
            swing_L = low(curr_bar);
            Print("We do have a swing low at: ", curr_bar, " L: ", low(curr_bar));
            drawswing(TimeToString(time(curr_bar)), time(curr_bar), low(curr_bar), 32, clrRed, +1);
        }
    }

然后,我们继续搜索波段高点和波段低点,但在此之前,如果检测到新的柱形,函数会在定义的 'length' 长度范围内检查周围的柱形,以确定是否存在波段高点或波段低点。变量 length 用于定义搜索波动的蜡烛图范围,对于范围内的每个柱形,函数都会将当前柱形的高点/低点与相邻柱形进行比较。

如果当前柱形的高点不高于周围的柱形,那么它就不是波段高点。如果当前柱形的低点不低于周围的柱形,那么它就不是波段低点。如果满足了所有这些条件,那么就可以确认波段高点或波段低点,然后存储相应的值,然后使用 'drawswing' 函数(绘制波段高点或波段低点)在图表上绘制标记。

    int values = CopyBuffer(handle, 0, 0, 2, buffer);
    if (values != 2) {
        Print("Failed to get indicator values");
        return;
    }

    Comment("Buffer[0]: ", buffer[0],
            "\nBuffer[1]: ", buffer[1]);
  • copyBuffer(handle, 0, 0, 2, buffer):将最新的 RSI 值复制到缓冲区。
  • 如果函数无法获取 RSI 值,则退出。
  • RSI 值将作为注释显示在图表上。
    int cntBuy = 0, cntSell = 0;
    if (!countOpenPositions(cntBuy, cntSell)) {
        return;
    }

'countOpenPositions(cntBuy, cntSell) 这个函数计算未平仓的买入和卖出仓位的数量,如果函数失败, 'OnTick' 退出。

    if (swing_H > 0 && Ask > swing_H && buffer[0] >= 70) {
        Print("Sell Signal: Market is above previous high and RSI >= 70");
        int swing_H_index = 0;
        for (int i = 0; i <= length * 2 + 1000; i++) {
            if (high(i) == swing_H) {
                swing_H_index = i;
                break;
            }
        }
        drawBreakLevels(TimeToString(time(0)), time(swing_H_index), high(swing_H_index), time(0), high(swing_H_index), clrBlue, -1);

        if (cntSell == 0) {
            double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);
            double sl = Bid + stopLoss * _Point;
            double tp = Bid - takeProfit * _Point;
            trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, lotsize, currentTick.bid, sl, tp, "RSI EA");
        }

        swing_H = -1.0;
        return;
    }

正如我之前所解释的,我们将检查卖出信号。其逻辑是,卖出价必须高于前一个波动高点。此外,RSI 值必须大于或等于 70 水平。如果条件得到满足,它就会在图表上标出波段高点并画出突破水平线。如果没有未平仓的卖出仓位,它就会开启一个卖出仓位,这个卖出交易将以计算出的止损和止盈水平开启,然后会重置波段高点值。

    if (swing_L > 0 && Bid < swing_L && buffer[0] <= 30) {
        Print("Buy Signal: Market is below previous low and RSI <= 30");
        int swing_L_index = 0;
        for (int i = 0; i <= length * 2 + 1000; i++) {
            if (low(i) == swing_L) {
                swing_L_index = i;
                break;
            }
        }
        drawBreakLevels(TimeToString(time(0)), time(swing_L_index), low(swing_L_index), time(0), low(swing_L_index), clrRed, +1);

        if (cntBuy == 0) {
            double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
            double sl = Ask - stopLoss * _Point;
            double tp = Ask + takeProfit * _Point;
            trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, lotsize, currentTick.ask, sl, tp, "RSI EA");
        }

        swing_L = -1.0;
        return;
    }
}

对于买入信号,使用相反的逻辑,如果买入价低于前一波段低点,且 RSI 值小于或等于 30 水平,那么技术上买入逻辑成立,条件满足。当满足条件时,它会标记出波动的低点,然后在图表上画出一个突破水平线。  如果没有未平仓的买入仓位,它就会根据计算出的止损和止盈水平开启新的买入交易,最后会重置波动低点值。

我们想要完成的目标如下:

_ 符合所有条件的卖出交易:

卖出交易


_ 满足所有条件的买入交易:

买入交易


OnTick 函数的总结:

EA 交易会在每个市场分时报价上执行以下关键操作

  1. 检测新柱形:它首先会检查上一个分时报价之后是否有新的蜡烛柱形成,然后会跟踪所有蜡烛柱。
  2. 识别波动高点和波动低点:然后,当市场价格高于波段高点或低于波段低点时,它就会确定市场的波段点,作为以后的参考水平。
  3. 获取 RSI 值:获取最新的 RSI 值,用于信号确认。
  4. 计数开启的仓位:跟踪当前未平仓的买入和卖出仓位。
  5. 生成交易信号:使用波段点,即波段高点、波段低点和 RSI 水平来生成买入或卖出信号。
  6. 执行交易:如果条件全部满足,则根据生成的信号开启新仓位。

最高价、最低价和时间值的自定义函数

double high(int index){
       return (iHigh(_Symbol, _Period, index));
}

double low(int index){
       return (iLow(_Symbol, _Period, index));
}

datetime time(int index){
       return (iTime(_Symbol, _Period, index));
}

函数 high(int index) 返回在指定索引处柱形的最高价。它将 index 作为 "int" 类型的参数。内置的 MQL5 函数 iHigh(_symbol,_Period,index) 用于获取当前交易品种和周期在索引处的最高价格。接着是 low(int index),该函数返回指定索引处的最低价,它还将 index 作为参数,其类型为 int,然后是 iLow(_symbol,_Period,index)。它也是 MQL5 的内置函数,用于获取当前交易品种在 index 柱的低价。最后是 time (int index) 函数,它返回指定索引柱形的时间,iTime(_symbol, _Period, index) 也是 MQL5 的内置函数,用于获取当前交易品种和周期在 index 处的时间,iTime 返回的数据类型为 datetime 类型。

绘制波段点的函数

void drawswing(string objName, datetime time, double price, int arrCode, color clr, int direction){
   if(ObjectFind(0, objName) < 0){
      ObjectCreate(0, objName, OBJ_ARROW, 0, time, price);
      ObjectSetInteger(0, objName, OBJPROP_ARROWCODE, arrCode);
      ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 10);
      
      if(direction > 0){ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_TOP);}
      if(direction < 0){ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_BOTTOM);}
      
      string text = "";
      string Descr = objName + text;
      ObjectCreate(0, Descr, OBJ_TEXT, 0, time, price);
      ObjectSetInteger(0, Descr, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, Descr, OBJPROP_FONTSIZE, 10);
      
      if(direction > 0){
         ObjectSetString(0, Descr, OBJPROP_TEXT,"  "+text);
         ObjectSetInteger(0, Descr, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
      }
      if(direction < 0){
         ObjectSetString(0, Descr, OBJPROP_TEXT,"  "+text);
         ObjectSetInteger(0, Descr, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
      }
   }
   ChartRedraw(0);
}

该函数为图表上的波动点(高点和低点)创建可视化标记。它需要以下参数:

  • ObjName:是要创建对象的名称。
  • Time:发生或检测到波段的时间。
  • Price:形成波段的价格。
  • ArrCode:用于可视化表示的箭头代码。
  • Clr:箭头的颜色。
  • Direction:波动方向(高点为正,低点为负)。

功能

1.对象创建:

  • ObjectFind(0, objName) < 0:检查给定名称的对象是否已经存在。
  • ObjectCreate(0, objName, OBJ-ARROW, 0, time, price):按指定的时间和价格创建箭头对象。
  • ObjectSetInteger(0, objName, OBJPROP-ARROWCODE, arrCode):设置箭头代码。
  • ObjectSetInteger(0, objName, OBJPROP-COLOR, clr):设置箭头颜色。
  • ObjectSetInteger(0, objName, OBJPROP-FONTSIZE, 10):设置字体大小。

2.方向处理:

  • 根据方向设置锚点位置。
  • OBJPROP-ANCHOR:设置箭头锚点的位置。

3.创建文本对象:

  • 创建与箭头相关联的文本对象,以显示附加信息。
  • 根据方向为文本设置颜色、字体大小和锚点。

4.图表更新:

  • ChartRedraw(0):重绘图表以反映变化

绘制断点水平线的函数

void drawBreakLevels(string objName, datetime time1, double price1, datetime time2, double price2, color clr, int direction){
   if(ObjectFind(0, objName) < 0){
         ObjectCreate(0, objName, OBJ_ARROWED_LINE, 0, time1, price1, time2, price2);
         ObjectSetInteger(0, objName, OBJPROP_TIME, 0, time1);
         ObjectSetDouble(0, objName, OBJPROP_PRICE, 0, price1);
         ObjectSetInteger(0, objName, OBJPROP_TIME, 1, time2);
         ObjectSetDouble(0, objName, OBJPROP_PRICE, 1, price2);
         ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
         
         string text = "Break";
         string Descr = objName + text;
         ObjectCreate(0, Descr, OBJ_TEXT, 0, time2, price2);
         ObjectSetInteger(0, Descr, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, Descr, OBJPROP_FONTSIZE, 10);  
         
         if(direction > 0){
            ObjectSetString(0, Descr, OBJPROP_TEXT,text+"  ");
            ObjectSetInteger(0, Descr, OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
         }
         if(direction < 0){
            ObjectSetString(0, Descr, OBJPROP_TEXT,text+"  ");
            ObjectSetInteger(0, Descr, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
         }          
   }
   ChartRedraw(0);
}

该函数用于创建价格水平突破的可视化表示,这些价格水平是之前在图表上检测到的波动点,它具有以下参数:

  • objName:要创建对象的名称。
  • time1, time2:开始时间是指波段形成的时间,结束时间是指发生突破的时间。
  • price1, price2:起始价是出现波段高点或波段低点的价格,price2 是突破波段高点或波段低点的价格。
  • Clr:箭头的颜色。
  • Direction:锚定文本的方向。


功能

1.对象创建:

  • ObjectFind(0, objName) < 0:检查给定名称的对象是否已经存在。
  • ObjectCreate(0, objName, OBJ_ARROWED_LINE, 0, time1, price1, time2, price2):在指定的时间和价格处创建箭头对象。
  • 设置箭头线起点和终点的时间和价格。
  • ObjectSetInteger(0, objName, OBJPROP-COLOR, clr):设置线的颜色。
  • ObjectSetInteger(0, objName, OBJPROP-WIDTH, 2):设置线宽。

2.创建文本对象:

  • 创建与该线相关联的文本对象,以显示附加信息。
  • 根据方向设置文本的颜色、字体大小和锚点。

3.图表更新:

  • chartRedraw(0):重绘图表以反映变化。


    结论

    总之,自定义函数提供了便捷的访问方式,简化了对柱形的最高值、最低值和时间值的访问,从而提升了交易体验。通过将 RSI 指标与 SMC 概念相结合,我们可以将指标可视化,并确认 EA 是否遵循了指令。该函数在图表上绘制箭头线时,会标记重要的点,也就是波段点(高点和低点)。此外,我们还可以观察到突破水平。我们还有动态更新功能,可确保图表实时更新最新的标记和指标,帮助进行可视化分析和决策。

    通过本综合指南,我们可以全面了解如何将 SMC 概念与 RSI 指标整合到任何 EA 交易系统的结构和功能中。通过循序渐进的讲解,读者现在应该可以清楚地掌握 SMC_RSI EA 交易系统的操作,从初始化变量到根据计算信号执行交易。无论您是经验丰富的交易者还是初学者,这篇全面的指南都能让您掌握必要的知识,在交易中有效地利用和定制这一强大的工具。

    下面是回溯测试结果,可以说 EA 本身还需要某种优化才能获得更高的利润率,下面是回溯测试结果:

    回溯测试

    下面我们可以看到净值曲线的可视化表示,我只测试了12个月,所以不能准确地说,如果测试可能持续12年,它的表现会如何。

    净值曲线

    参考

    原文:https://www.mql5.com/zh/articles/15017

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

    附加的文件 |
    SMC_RSI.mq5 (12.13 KB)
    构建蜡烛图趋势约束模型(第7部分):为EA开发优化我们的模型 构建蜡烛图趋势约束模型(第7部分):为EA开发优化我们的模型
    在本文中,我们将详细探讨为开发专家顾问(EA)所准备的指标的相关内容。我们不仅会讨论如何对当前版本的指标进行进一步改进,以提升其准确性和功能,还会引入全新的功能来标记退出点,以弥补之前版本仅具备识别入场点功能的不足。
    通过推送通知监控交易——一个MetaTrader 5服务的示例 通过推送通知监控交易——一个MetaTrader 5服务的示例
    在本文中,我们将探讨如何创建一个服务应用程序,用于向智能手机发送关于交易结果的通知。我们将学习如何处理标准库对象列表,以便根据所需属性组织对象的选择。
    在MQL5中创建动态多品种、多周期相对强弱指数(RSI)指标仪表盘 在MQL5中创建动态多品种、多周期相对强弱指数(RSI)指标仪表盘
    本文中,我们将在MQL5中开发一个动态多品种、多周期相对强弱指数(RSI)指标仪表盘,为交易者提供跨不同品种和时间段的实时RSI值。该仪表盘具备交互式按钮、实时更新功能和有色编码的指标,以帮助交易者做出明智的决策。
    特征向量和特征值:MetaTrader 5 中的探索性数据分析 特征向量和特征值:MetaTrader 5 中的探索性数据分析
    在这篇文章中,我们将探索特征向量和特征值在探索性数据分析中的不同应用方式,以揭示数据中的独特关系。