English Русский Español Deutsch 日本語 Português
preview
构建蜡烛图趋势约束模型(第7部分):为EA开发优化我们的模型

构建蜡烛图趋势约束模型(第7部分):为EA开发优化我们的模型

MetaTrader 5交易 | 4 二月 2025, 13:23
297 0
Clemence Benjamin
Clemence Benjamin

文章主要内容:


        引言

        基于现有的指标,开发一个专家顾问(EA)是可行的。在本文中,我将提及两种方法:

        1. 将指标条件编码到EA算法中既高效又快速,即使在策略测试器中也是如此使用这种方法,EA无需单独使用指标即可运行。
        2. 另一种方法是准备一个关注指标缓冲区的EA算法。如果缓冲区为True或False,EA会按照既定方式作出响应。在MetaTrader 5平台上,系统要运行,必须有两个文件:EA和指标都必须存在于平台路径中的特定目录中。发布一个运行自定义指标的EA具有挑战性,因为当尝试在MQL5社区发布时,验证系统找不到该指标。我当然尝试过,但遇到了错误,所以只能在我的电脑上运行,无法发布。

        在进一步探讨之前,我们需要为EA任务准备指标。这包括确保我们的指标缓冲区组织得当,并了解它们的操作,以便更容易地将这个概念开发成EA。上述两种方法都效果很好,各有优缺点,我们将在后续文章中讨论。使用指标与EA结合的主要优势在于,它降低了编写EA算法的复杂性。开发人员可以专注于算法的少数几个特定部分,因为条件已经编码到指标程序中。

        在第六部分中,Trend Constraint V1.08是我们的最新版本,它将两个主要集成合并到一个程序中。我们对这一进展感到满意,因为现在我们可以轻松地在Telegram和WhatsApp上获取信号。然而,还有一些问题需要解答:

        • 是的,我们现在正在接收信号,但这些信号是最好的吗?
        • 我们可以执行交易,但何时退出呢?

        这些问题只能通过重新查看图表,了解指标的历史表现,并重新编写源代码以添加新功能或增强当前系统来解决。在完善当前的EA时,我提议:

        1. 尽可能彻底地了解每个缓冲区的目的。
        2. 重新引入移动平均线交叉作为入场信号。
        3. 使用风险报酬比绘制矩形:绿色表示盈利目标范围,红色表示损失范围。

        我们的目标是设计指标以显示入场和出场概念,让交易者可以手动跟随。如果策略可以手动执行,那么它也可以自动化。风险回报比(RRR)有助于交易者评估交易的潜在盈利相对于其风险的大小。我们将讨论如何通过在我们的指标中融入新功能来概述潜在的出场位置,包括RRR的公式,即:

         


        风险回报比,公式。

        其中:

        RRR表示风险回报比;
        潜在损失是指如果交易或投资走势与你的预期相反,你可能会损失的金额;
        潜在收益是指如果交易或投资走势与你的预期一致,你期望获得的金额。

        让我来给出一个例子:

        假设你正在考虑以50美元的价格购买一只股票。你设置了48美元的止损价(表示每股潜在损失2美元),并设定了56美元的目标价(表示每股潜在收益6美元)。计算这只股票的风险收益比。

        • 潜在损失:50美元(买入价)- 48美元(止损价)= 2美元
        • 潜在收益:56美元(目标价)- 50美元(买入价)= 6美元

        将这些值代入公式:风险收益比 = 1/3

        具体来说,这意味着你每承担1美元的风险,预期能收获3美元的收益。较低的比率(例如1:1或更低)表明相对于潜在收益,风险更大,而较高的比率则表明风险收益情况更为有利。

        基于上述示例,以下是一个使用矩形的图示:红色矩形表示风险,绿色矩形表示收益。

        风险收益比图示


        接下来,我们将在文章的后续部分进一步讨论这个话题。通过本次讨论,我们旨在重点关注以下几点:

        1. 了解风险管理在算法交易中的重要性。
        2. 实施风险收益比及其数学基础。
        3. 开发动态退出策略以实现最佳交易管理。
        4. 增强视觉指标以做出更好的交易决策。
        5. 测试和验证指标在实际应用中的效果。


        识别当前系统的限制

        为了推进指标系统的新功能开发,重新审视关键部分以识别当前系统存在的不足之处至关重要。为实现这一目标,我根据自己的方法考虑了两种技术,这将指导我识别和解决问题。这些技术包括:

        1. 重新审视图表:这涉及回顾指标图表窗口的历史记录,注意其展示方式,并识别任何异常。  
        2. 重新审视代码:这是第二阶段:在完成第一步后,我们将检查与已识别问题相关的代码部分,以对其进行修复。


        再次审视图表

        以下是Boom 500指数图表的图像。我将概述在图表上发现的问题:


        Boom 500指数 M1


        • 从图表中,(A)代表一个尖峰蜡烛图。在这种情况下,箭头显示在蜡烛下方,而它应该显示在蜡烛上方。这个问题出现是因为箭头代表RSI的超买区域,这只有在蜡烛收盘后才会出现。
        • DRAW_LINE显示功能运行良好,通过观察线条的颜色变化,我们可以识别出新的趋势。我们希望它能发出摆动交易信号,因为通常市场改变方向时,往往会有较大的波动。
        • 蓝色买入箭头信号代表潜在趋势的延续,并目前指示在主要上升趋势中RSI的超卖区域。请记住,我们的主题是将信号限制在D1蜡烛图(即日线图)和市场情绪上。虽然相对强弱指数(RSI)的极端水平可能是较好的入场点,但它们通常只表示市场区域,而非确定的反转。无论RSI水平如何,市场都可能继续维持原有趋势。我建议重新引入移动平均线交叉来寻找其他入场点,以最大化收益,因为这通常与价格走势相吻合。
        • 我还设想,在指标发出入场信号时,绘制用于表示风险收益比的长方形会有好处,如前文介绍所述。这个新版本必须准确绘制长方形,以正确展示风险和收益。请查看下面图表截图中的想法:第一张是欧元兑美元(EURUSD),第二张是Boom 500,依次排列。

        EURUSD, M1: Euro vs US Dollar

        EURUSD


        Boom 500指数 M1:

        Boom 500指数 M1

        从这些图表中,我们可以清晰地观察到这一新方法。我们希望编写指标代码,以准确反映手动演示的内容。根据多位技术分析师和交易者的观点,传统上,枢轴点被视为设定止盈和止损的目标。因此,在图表所示的场景中,有时风险远大于潜在收益,1:3的风险收益比可能不再适用。

        综上所述,从上面的图示中我们可以得出:

        1. 枢轴点被用作设定出场点(包括止损和止盈)的参考点。
        2. 入场和出场位的价格可以在后续的逻辑中使用。  

        在下一部分,我们将检查代码中的关键区域并对其进行处理。


        再次审视代码:

        以下是可以进行修改的代码段:
        #property indicator_type1 DRAW_ARROW
        #property indicator_width1 5
        #property indicator_color1 0xFF3C00
        #property indicator_label1 "Buy" // We are going to change it to "Buy Zone"
        
        #property indicator_type2 DRAW_ARROW
        #property indicator_width2 5
        #property indicator_color2 0x0000FF
        #property indicator_label2 "Sell"// We are going to change it to "Sell Zone"


        这段程序的目的是确定箭头在K线图(蜡烛图)上的位置。这涉及到Buffer 2的迭代函数。为了让指标显示在K线的高点,我们需要将Low[i]更改为High[i];

        //Indicator Buffer 2 // We are going to set the indicator to display at candlestick high by changing the highlighted text to High[i]
              if(RSI[i] > Overbought
              && RSI[i+1] < Overbought //Relative Strength Index crosses above fixed value
              && Open[barshift_M1[i]] <= Close[1+barshift_D1[i]] //Candlestick Open <= Candlestick Close
              && MA[i] < MA2[i] //Moving Average < Moving Average
              && MA3[i] < MA4[i] //Moving Average < Moving Average
              )
        
                {
                 Buffer2[i] = Low[i]; //Set indicator value at Candlestick Low // change to High[i]
                 if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell Zone"); //Alert on next bar open
                 time_alert = Time[1];
                }
              else
                {
                 Buffer2[i] = EMPTY_VALUE;
                }



        融入移动平均线交叉:

        让我们通过讨论下面的代码片段来揭示向程序中添加新功能的流程。除了现有的缓冲区之外,我们还将添加两个新的缓冲区,即Buffer 6和Buffer 7。

        根据我们智能程序的逻辑,我们将首先定义其属性。由于我们最近为RSI指标采用了“卖出区”和“买入区”,因此这些将成为指标的新“卖出”和“买入”信号。

        #property indicator_type6 DRAW_ARROW
        #property indicator_width6 1
        #property indicator_color6 0x0000FF
        #property indicator_label6 "Sell"
        
        #property indicator_type7 DRAW_ARROW
        #property indicator_width7 1
        #property indicator_color7 0xFFAA00
        #property indicator_label7 "Buy"

        我们需要设置可自定义的输入参数,以优化出最佳信号。我们将采用7期移动平均线和21期移动平均线。

        input int Entry_MA_fast = 7 ;
        input int Entry_MA_slow = 21 ;

        OnCalculate函数的新功能如下所示。它详细说明了当条件满足时,程序将如何显示结果。

        SetIndexBuffer(5, Buffer6);
           PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, EMPTY_VALUE);
           PlotIndexSetInteger(5, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
           PlotIndexSetInteger(5, PLOT_ARROW, 242);
           SetIndexBuffer(6, Buffer7);
           PlotIndexSetDouble(6, PLOT_EMPTY_VALUE, EMPTY_VALUE);
           PlotIndexSetInteger(6, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
           PlotIndexSetInteger(6, PLOT_ARROW, 241);


        这个阶段融入了移动平均线交叉的条件。在此场景下,Buffer 7 的条件与 Buffer 6 的条件互为反向。简而言之,两者的处理流程是一致的,但正好反过来。

        //Indicator Buffer 6
              if(MA8[i] < MA9[i]
              && MA8[i+1] > MA9[i+1] //Moving Average crosses below Moving Average
              && Open2[i] <= Close[1+barshift_D1[i]] //Candlestick Open <= Candlestick Close
              )
                {
                 Buffer6[i] = High[i]; //Set indicator value at Candlestick High
                 if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell"); //Alert on next bar open
                 time_alert = Time[1];
                }
              else
                {
                 Buffer6[i] = EMPTY_VALUE;
                }
              //Indicator Buffer 7
              if(MA8[i] > MA9[i]
              && MA8[i+1] < MA9[i+1] //Moving Average crosses above Moving Average
              && Open2[i] >= Close[1+barshift_D1[i]] //Candlestick Open >= Candlestick Close
              )
                {
                 Buffer7[i] = Low[i]; //Set indicator value at Candlestick Low
                 if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy"); //Alert on next bar open
                 time_alert = Time[1];
                }
              else
                {
                 Buffer7[i] = EMPTY_VALUE;
                }


        绘制矩形

        我非常享受这次代码编写的过程。我考虑在现有程序的基础上进行扩展,通过修改最近加入的缓冲区来增添标记风险和盈利区域矩形的功能。首先,我在纸上大致规划了这些对象的放置逻辑以及它们将与哪些缓冲区(特别是Buffer 6和Buffer 7)相关联。

        以下是代码实现前的逻辑规划:

         当快速移动平均线下穿慢速移动平均线时: 

        若Buffer7的条件满足,我们希望在当前蜡烛图低点下方放置一个矩形。
          • 矩形的宽度应为5根K线或以上。
          • 矩形的高度向下延伸X点;其中,X代表点数的数量。
          • 矩形必须为绿色。
          • 在绿色矩形之上,当Buffer7的条件满足时,放置一个高度为绿色矩形1/3的红色矩形。
          • 所有参数必须可自定义。

        当快速移动平均线上穿慢速移动平均线,且Buffer 6的条件满足时,我们希望在当前蜡烛图高点上方放置一个矩形。

        以下是具体规格说明:

          • 矩形的宽度应为5根K线或以上。
          • 矩形的高度向上延伸X点,其中X代表点数的数量。
          • 矩形必须为绿色。
          • 在绿色矩形之下,当Buffer 6的条件满足时,放置一个高度为绿色矩形1/3的红色矩形。
          • 所有参数必须可自定义。

        首先,我们需要能够输入自定义参数的功能。以下是展示参数自定义功能的代码片段:

        //--- new inputs for rectangles
        input int RectWidth = 5;                 // Width of the rectangle in bars
        input int RectHeightPointsBuy = 50;      // Height of the profit rectangle in points for Buy
        input int RectHeightPointsSell = 50;     // Height of the profit rectangle in points for Sell
        input color ProfitRectColor = clrGreen;  // Color of the profit rectangle
        input color RiskRectColor = clrRed;      // Color of the risk rectangle
        
        默认情况下,我已将点数设置为50,但您可以根据自己的需求和盈利目标进行调整。我还加入了一个功能,该功能可以自动根据盈利目标按比例调整风险矩形的高度。这一点非常重要,因为它能让指标适应任何背景颜色的变化。例如,如果您使用绿色图表背景,绿色矩形可能会融入其中而变得难以察觉,而黄色背景则会提供更好的对比度。自定义功能至关重要,它能确保我们对所使用的工具拥有完全的控制权。


        现在,让我们来检查OnCalculate函数,看看迭代是如何进行的。

        if(RSI[i] > Overbought) {
                if(close[i] > MA[i])
                    Buffer6[i] = close[i] - pips * myPoint;
            }
        
            if(RSI[i] < Oversold) {
                if(close[i] < MA[i])
                    Buffer7[i] = close[i] + pips * myPoint;
            }
        
            if(Buffer6[i] > 0) {
                Buffer1[i] = close[i] - pips * myPoint;
                Buffer3[i] = close[i] - pips * myPoint;
                if (Buffer6[i - 1] < 0) {
                    myAlert("indicator", "Sell Signal Detected!");
                    if (Audible_Alerts)
                        Alert(Symbol(), " ", Period(), ": Sell Signal Detected!");
        
                    // Create profit rectangle for Sell
                    double highProfitRect = close[i];
                    double lowProfitRect = close[i] - RectHeightPointsSell * myPoint;
                    string profitRectName = "SellProfitRect" + IntegerToString(i);
                    if (ObjectFind(0, profitRectName) != 0) {
                        ObjectCreate(0, profitRectName, OBJ_RECTANGLE, 0, time[i], highProfitRect, time[i + RectWidth], lowProfitRect);
                        ObjectSetInteger(0, profitRectName, OBJPROP_COLOR, ProfitRectColor);
                        ObjectSetInteger(0, profitRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, profitRectName, OBJPROP_WIDTH, 2);
                    }
        
                    // Create risk rectangle for Sell
                    double highRiskRect = close[i];
                    double lowRiskRect = close[i] + (RectHeightPointsSell / 3) * myPoint;
                    string riskRectName = "SellRiskRect" + IntegerToString(i);
                    if (ObjectFind(0, riskRectName) != 0) {
                        ObjectCreate(0, riskRectName, OBJ_RECTANGLE, 0, time[i], highRiskRect, time[i + RectWidth], lowRiskRect);
                        ObjectSetInteger(0, riskRectName, OBJPROP_COLOR, RiskRectColor);
                        ObjectSetInteger(0, riskRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, riskRectName, OBJPROP_WIDTH, 2);
                    }
                }
            }
        
            if(Buffer7[i] > 0) {
                Buffer2[i] = close[i] + pips * myPoint;
                Buffer4[i] = close[i] + pips * myPoint;
                if (Buffer7[i - 1] < 0) {
                    myAlert("indicator", "Buy Signal Detected!");
                    if (Audible_Alerts)
                        Alert(Symbol(), " ", Period(), ": Buy Signal Detected!");
        
                    // Create profit rectangle for Buy
                    double highProfitRect = close[i] + RectHeightPointsBuy * myPoint;
                    double lowProfitRect = close[i];
                    string profitRectName = "BuyProfitRect" + IntegerToString(i);
                    if (ObjectFind(0, profitRectName) != 0) {
                        ObjectCreate(0, profitRectName, OBJ_RECTANGLE, 0, time[i], highProfitRect, time[i + RectWidth], lowProfitRect);
                        ObjectSetInteger(0, profitRectName, OBJPROP_COLOR, ProfitRectColor);
                        ObjectSetInteger(0, profitRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, profitRectName, OBJPROP_WIDTH, 2);
                    }
        
                    // Create risk rectangle for Buy
                    double highRiskRect = close[i] - (RectHeightPointsBuy / 3) * myPoint;
                    double lowRiskRect = close[i];
                    string riskRectName = "BuyRiskRect" + IntegerToString(i);
                    if (ObjectFind(0, riskRectName) != 0) {
                        ObjectCreate(0, riskRectName, OBJ_RECTANGLE, 0, time[i], highRiskRect, time[i + RectWidth], lowRiskRect);
                        ObjectSetInteger(0, riskRectName, OBJPROP_COLOR, RiskRectColor);
                        ObjectSetInteger(0, riskRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, riskRectName, OBJPROP_WIDTH, 2);
                    }
                }
            
        

        这里是创建矩形的地方:

        盈利矩形:

        • 对于卖出信号,一个绿色矩形从收盘价向下延伸。
        • 对于买入信号,一个绿色矩形从收盘价向上延伸。


        风险矩形:

          • 对于卖出信号,一个红色矩形从收盘价向上延伸,其高度为盈利矩形的1/3。
          • 对于买入信号,一个红色矩形从收盘价向下延伸,其高度为盈利矩形的1/3。

        现在让我们简要解释一下新引入的函数;

        • ObjectFind:用于检查图表上是否存在具有特定名称的对象的函数。

        int ObjectFind(
           long   chart_id,   // Chart ID (0 means the current chart)
           string name        // Name of the object to search for
        );
        


        • ObjectCreate:用于在图表上创建新的图形对象的函数。可以指定对象的类型(例如,矩形、趋势线等)。

        bool ObjectCreate(
           long    chart_id,   // Chart ID (0 means the current chart)
           string  name,       // Name of the object to create
           ENUM_OBJECT type,   // Type of the object (e.g., OBJ_RECTANGLE, OBJ_TREND, etc.)
           int     sub_window, // Number of the subwindow (0 means the main chart window)
           datetime time1,     // First coordinate time
           double  price1,     // First coordinate price
           ...                 // Additional coordinates depending on the object type
        );
        


        • ObjectSetInteger:用于设置图形对象的整数属性(如颜色、样式和宽度)的函数。

        bool ObjectSetInteger(
           long   chart_id,     // Chart ID (0 means the current chart)
           string name,         // Name of the object
           int    prop_id,      // ID of the property to set (e.g., OBJPROP_COLOR, OBJPROP_STYLE, etc.)
           long   value         // Value of the property
        );
        

        该函数返回一个布尔型值;

        • true:如果属性被成功设置。
        • false:如果属性设置失败。

        对于上述函数的更深入解释,请随时查阅MQL5文档,它涵盖了MQL5语言的所有内容,内容丰富。 


        介绍出场点位

        引入出场点位是Trend Constraint V1.08版本的一个关键改进。有效的出场策略不仅能保护利润,还能将损失降到最低。我们建议基于关键支撑位和阻力位整合预定义的出场点位,这对于识别潜在的反转区域至关重要。之前的信号逻辑仅涉及货币对的名称,但新的改进将提供所有相关价格,包括入场价、止损价和止盈价。通过使用这些价位,交易者可以确定出场点位,即价格可能反转或遇到重大阻力的位置,从而优化他们的出场策略。


        以下是具体方法:

        为了修改程序以在矩形附近插入价位线,我们将在特定价位水平添加三条线:

        • 信号价位(信号发生时的收盘价), 
        • 盈利目标价位, 
        • 以及风险目标价位。 

        这些线将在检测到信号后立即放置。我们还将设置警报,以通知用户这些特定的价位水平。

        首先,我们将盈利点风险点定义为可自定义的输入参数。

        input double profitPoints = 60; // Points for profit target
        input double riskPoints = 20;   // Points for risk target
        

        在此,让我介绍用于放置线条的自定义函数。下面的代码片段展示了这些函数的定义。如果你对MQL5还不太了解,请注意,这里使用void来表示函数不返回任何值。 

        void CreatePriceLine(string name, color lineColor, double price, datetime time) {
            if (ObjectFind(0, name) == -1) {
                ObjectCreate(0, name, OBJ_HLINE, 0, time, price);
                ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
                ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
            } else {
                ObjectMove(0, name, 0, time, price);
            }
        }
        
        void PlaceSignalLines(double signalPrice, double profitTarget, double riskTarget, datetime time) {
            CreatePriceLine("SignalPriceLine", clrBlue, signalPrice, time);
            CreatePriceLine("ProfitTargetLine", clrGreen, profitTarget, time);
            CreatePriceLine("RiskTargetLine", clrRed, riskTarget, time);
        }
        


        最后,这里是信号检测的逻辑。如你所见,我们设置了警报来指示关键价位,这将帮助交易者在手动执行程序时做出决策。 

        void CheckSignalAndPlaceLines() {
            for (int i = rates_total - 2; i >= 0; i--) {
                if (Buffer6[i] != 0.0) { // Buy Signal Detected
                    double signalPrice = Close[i];
                    double profitTarget = signalPrice + profitPoints * Point;
                    double riskTarget = signalPrice - riskPoints * Point;
                    PlaceSignalLines(signalPrice, profitTarget, riskTarget, Time[i]);
                    Alert("Buy Signal: Signal Price = ", signalPrice, " Profit Target = ", profitTarget, " Risk Target = ", riskTarget);
                }
                if (Buffer7[i] != 0.0) { // Sell Signal Detected
                    double signalPrice = Close[i];
                    double profitTarget = signalPrice - profitPoints * Point;
                    double riskTarget = signalPrice + riskPoints * Point;
                    PlaceSignalLines(signalPrice, profitTarget, riskTarget, Time[i]);
                    Alert("Sell Signal: Signal Price = ", signalPrice, " Profit Target = ", profitTarget, " Risk Target = ", riskTarget);
                }
            }
        }
        


        总的来说,我们对程序进行了三项主要改进,下面我将概述这些优化:

        1. 我们对Trend Constraint V1.08进行了优化,并引入了移动平均线交叉作为入场信号。
        2. 我们讨论了使用矩形来表示风险和盈利区域的方法。
        3. 此外,我们还讨论了出场点位在我们程序中的重要性。

        从总结中可以看出,随着我们不断对程序进行完善,可能会出现三个版本的程序。我们现在有了Trend Constraint V1.09、V1.10和V1.11。接下来,我们将讨论测试性能和结果。


        测试和验证

        V1.09版本的测试与编译取得了成功,尽管在过程中遇到了一些问题,但都已得到圆满解决。以下是一些图片,展示了面板成功启动的画面,以及一个性能分析器摘要,详细列出了CPU上各函数的性能表现。


        Trend Constraint V1.09启动。


        性能分析器测试结果:

        MetaEditor性能分析器


        在取得上述成果之前,我们确实遇到了一些错误,但随后这些错误都得到了解决。我很乐意与大家分享这个解决问题的过程。请查看下面的图片:


        错误日志


        在仔细阅读了错误日志后,我们更容易地检查了程序,并找出了缺失的部分。在添加移动平均线交叉功能时,我们除了现有的变量外,还需要为新缓冲区声明MA8、MA9、MA_handle8和MA_handle9。下面的代码展示了所做的声明。高亮显示的行是之前导致错误的代码。

        int RSI_handle;
        double RSI[];
        double Open[];
        double Close[];
        int MA_handle;
        double MA[];
        int MA_handle2;
        double MA2[];
        int MA_handle3;
        double MA3[];
        int MA_handle4;
        double MA4[];
        double Low[];
        double High[];
        int MA_handle5;
        double MA5[];
        int MA_handle6;
        double MA6[];
        int MA_handle7;
        double MA7[];
        int MA_handle8;
        double MA8[];
        int MA_handle9;
        double MA9[];
        double Open2[];

        在添加了高亮显示的声明后,程序成功编译。

        接下来是V1.10版本,在加入了矩形元素后,程序成功编译,但图表上没有任何显示。很难确定我们遗漏了什么,因此我们将继续逐行调试,直到问题解决,并在接下来的文章中分享我们的发现。

        V1.11版本遇到了编译错误,我将在接下来的文章中分享整个解决过程。

        在EA开发中,对程序中缓冲区的总结非常重要,因为它有助于我们了解哪个缓冲区执行了必要的条件。参见下表:

        缓冲区 说明
        Buffer1. 此缓冲区(Buffer1)用于识别以下情况:RSI(相对强弱指数)穿越某一固定值(超买区),当前开盘价大于或等于某一特定K线的收盘价,两个移动平均线(MA和MA3)均大于其各自的对比移动平均线(MA2和MA4),并在图表上标记一个“买入区”。
        Buffer2.
        此缓冲区用于识别以下情况:RSI穿越某一固定值(超买区),当前开盘价小于或等于某一特定K线的收盘价,两个移动平均线(MA和MA3)均小于其各自的对比移动平均线(MA2和MA4),并在图表上标记一个“卖出区”。
        Buffer3.
        此缓冲区用于识别MA5穿越MA6的情况,并在图表上标记一个“买入摆动点”。
        Buffer4.
        此缓冲区用于识别MA5穿越至MA6下方的情况,并在图表上标记一个“卖出摆动点”。
        Buffer5.
        此缓冲区用于跟踪MA3是否大于MA7。
        Buffer6.
        此缓冲区用于识别MA8穿越至MA9下方的情况,并在图表上标记一个“卖出”信号。
        Buffer7.
        此缓冲区用于识别MA8穿越MA9的情况,并在图表上标记一个“买入”信号。
        Buffer8.  此缓冲区用于跟踪MA3是否小于MA7。


        结论

        总结而言,Trend Constraint的开发与完善标志着算法交易领域的一大进步。我们致力于解决当前系统的局限性,并引入策略的退出点,旨在打造一个更加稳健且高效的指标。通过纳入止损和止盈水平,以及增强的可视化表示,我们确保了该指标的用户友好性及其对不断变化的市场条件的适应性。这些功能如今能够清晰地可视化潜在的交易结果,从而辅助做出更佳决策。

        经过严格的测试和验证,我们已经证明了该指标的可靠性和有效性。尽管我们遇到了错误,但我们坚持不懈,并通过深入研究,成功解决了这些问题。我们将利用文档并与开发者社区协作,以解决任何剩余的问题。

        总体而言,经过增强的Trend Constraint为寻求以自信和精准的方式应对金融市场复杂性的交易者提供了一个全面的解决方案。其先进功能以及对风险管理和利润优化的战略方法使其成为任何交易者工具包中的宝贵补充。

        以下附上了用于您项目进一步开发的文件。祝您开发顺利!

        文件名 说明
        Trend Constraint V1.09 具有移动平均线交叉信号的改进程序。
        Trend Constraint V1.10 具有对象绘制功能的优化程序。仍处于调试阶段。


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

        附加的文件 |
        在MQL5中创建动态多品种、多周期相对强弱指数(RSI)指标仪表盘 在MQL5中创建动态多品种、多周期相对强弱指数(RSI)指标仪表盘
        本文中,我们将在MQL5中开发一个动态多品种、多周期相对强弱指数(RSI)指标仪表盘,为交易者提供跨不同品种和时间段的实时RSI值。该仪表盘具备交互式按钮、实时更新功能和有色编码的指标,以帮助交易者做出明智的决策。
        如何将聪明资金概念(SMC)与 RSI 指标结合到 EA 中 如何将聪明资金概念(SMC)与 RSI 指标结合到 EA 中
        聪明资金概念(结构突破)与 RSI 指标相结合,可根据市场结构做出明智的自动交易决策。
        重塑经典策略(第三部分):预测新高与新低 重塑经典策略(第三部分):预测新高与新低
        在系列文章的第三部分中,我们将通过实证分析经典交易策略,探讨如何利用人工智能进行优化。本次研究聚焦于运用线性判别分析模型(LDA)预测价格走势中的更高高点与更低低点。
        通过推送通知监控交易——一个MetaTrader 5服务的示例 通过推送通知监控交易——一个MetaTrader 5服务的示例
        在本文中,我们将探讨如何创建一个服务应用程序,用于向智能手机发送关于交易结果的通知。我们将学习如何处理标准库对象列表,以便根据所需属性组织对象的选择。