English Русский Deutsch 日本語
preview
构建K线趋势约束模型(第十部分):战略均线金叉与死叉(智能交易系统EA)

构建K线趋势约束模型(第十部分):战略均线金叉与死叉(智能交易系统EA)

MetaTrader 5示例 |
56 1
Clemence Benjamin
Clemence Benjamin

概述

在本文中,我们深入探讨如何将战略均线金叉与死叉策略融入趋势约束智能交易系统(EA),释放这些久经考验的移动平均线交叉技术的潜力。我们的目标是通过自动化这些策略,增强算法交易中的趋势跟踪能力,确保精确性、一致性和与更广泛交易系统的无缝兼容性。

在有效识别和利用强劲的看涨和看跌趋势方面,许多交易者都面临挑战。虽然金叉发出看涨动能信号,死叉表明看跌情绪,在手动交易中已展现出其价值,但由于缺乏自动化,常常导致错失机会和执行不一致。

通过将金叉和死叉策略融入自动化EA,交易者可以系统地执行反转信号,而不受其他趋势跟踪策略的限制。与依赖每日市场情绪受限的策略不同,这些交叉策略独立运作,以识别并把握反转时机。这确保了能更早地捕捉到潜在的转折点,提高响应速度和整体交易表现,同时与更广泛的趋势策略保持一致。

核心内容:

  1. 理解金叉与死叉策略
  2. 分析其在趋势跟踪整体背景下的相关性
  3. 趋势约束适应条件
  4. 在MQL5中实现该策略
  5. 初始策略测试
  6. 将新策略融入趋势约束中
  7. 测试和优化结果
  8. 结论


理解金叉与死叉策略

金叉与死叉策略是技术分析中的关键概念,用于根据移动平均线交叉情况发出潜在看涨或看跌市场趋势的信号。当短期移动平均线(通常是50日均线)上穿长期移动平均线(往往是200日均线)时,即形成金叉。这一事件被视为强烈的看涨信号,表明长期上升趋势可能即将开启。投资者和交易者可能会将此视为买入或持有股票的契机,预期价格将上涨。 

相反,死叉则是短期移动平均线下穿长期移动平均线的情形,预示着可能出现看跌趋势。这被视为市场可能进入持续下跌阶段的预警信号。死叉可能会促使投资者卖出或做空,或者至少保持谨慎态度,因为它暗示着市场环境正在转弱。这两种信号不仅用于直接交易决策,还用于更广泛的市场情绪分析,尽管它们并非万无一失,应与其他指标或分析方法结合使用,以确认趋势。


分析其在趋势跟踪整体背景下的相关性

在趋势跟踪策略的宏观背景下,金叉和死叉是识别市场趋势启动与潜在反转的基础工具。趋势跟踪是一种交易策略,交易者通过顺应既定趋势的方向进行交易,旨在从市场价格的持续波动中获利。以下是金叉和死叉如何融入这一框架:

1. 趋势识别:这些交叉的主要功能是为新趋势的开启提供明确信号。金叉标志着上升趋势的开始,促使趋势跟踪者建立多头头寸或增加持仓。相反,死叉则表明下降趋势的启动,提示是时候平掉多头头寸或考虑做空。
2. 趋势确认:这些信号在趋势跟踪策略中起到了确认作用。趋势跟踪者可能会将这些交叉与其他指标(如价格行为、成交量或动量指标)结合使用,以验证趋势是否确实正在形成。这种多指标方法有助于减少错误信号,这在趋势跟踪中至关重要,因为目标是尽可能长时间地跟随趋势。
3. 风险管理:在趋势跟踪中,风险管理是关键,因为趋势可能会意外反转或暂停。金叉和死叉可用于设定止损水平或决定何时减少或增加头寸规模。例如,金叉后价格跌破移动平均线可能表明需要收紧止损或减少持仓。
4. 长期视角:由于使用了较长期的移动平均线,这些信号本质上具有长期性,与趋势跟踪捕捉市场大波动而非短期波动的理念相契合。这种长期关注有助于过滤掉市场噪音,专注于显著的价格变动。
5. 适应性:尽管传统上使用50日和200日移动平均线,但该策略可根据不同时间框架或市场条件进行调整。在快速变动的市场中,交易者可能会使用较短的周期以获取更快的信号,而在更稳定的市场中,则可能偏好较长的周期以避免被小幅回调所误导。

然而,金叉和死叉在趋势跟踪中的相关性可进行如下批判性分析:

  • 滞后性:主要批判点之一是移动平均线带来的滞后性。到这些交叉发生时,趋势的显著部分可能已经发生,这可能会降低仅基于这些信号进行交易的盈利能力。
  • 错误信号:趋势跟踪本身就存在虚假突破或过早趋势反转的风险。金叉和死叉也不例外,因此,它们应成为更广泛策略的一部分,该策略应包括其他形式的分析或确认信号。
  • 市场环境:它们的有效性可能因市场条件而异。在趋势市场中,这些信号可能非常有效,但在震荡或波动较大的市场中,它们可能会导致多次错误启动。


趋势约束适应条件

在长期策略中,施加过多限制可能会影响其潜力。例如,考虑这样一种情形:当日市场情绪为看跌。而在同一天内,较低时间框架上出现的金叉信号可能预示着潜在反转,表明市场可能转为看涨。当此类反转发生时,其他受约束的策略通常会顺应新出现的趋势情绪,与新的市场方向保持一致。

为了利用这一动态变化,死叉和金叉策略可作为独立模块融入趋势约束EA中。这样一来,该EA便可通过捕捉与新出现趋势相契合的反转入场点,实现性能的最大化。

该方法可以确保:

  1. 跨时间框架的灵活性:该EA能够检测短期反转(如金叉),并将其与更广泛的市场走势相协调,从而提高适应性。
  2. 入场点优化:尽早识别趋势情绪的变化,使得EA能够采取战略性头寸,减少对市场变化的响应滞后。
  3. 策略间的协同作用:将金叉和死叉策略独立整合,使EA能够发挥它们的优势,同时不会凌驾于主要趋势跟踪机制之上。

通过采用这种双层策略,趋势约束EA能够在保持长期盈利能力的同时,有效应对市场反转。现在,让我们进入下一阶段,即实施并整合该策略。


在MQL5中实现该策略

在我的开发方法中,创建多策略EA时,我总是会先将每个策略设计成独立的EA。这样可以在将其合并到统一的代码库之前,进行有针对性的测试和优化。按照这种方法,我们将首先独立拆解并开发“战略性金叉与死叉EA”,然后再将其集成到主EA中。

和之前一样,从桌面打开MetaEditor 5应用程序,或者在MetaTrader 5终端中按F4键直接启动它。按照以下开发步骤进行操作。对于初学者,建议手动输入代码,而不是复制粘贴,这样有助于强化技能并加深理解。

我们从金叉和死叉策略这里开始:

头文件与基本信息:

这是程序的最上方部分,我们首先定义基本信息,以确保清晰性和专业性。基本信息将程序标识为“战略性金叉与死叉EA”,包括版权详情、描述和版本号。这一步骤有助于我们规划项目,并确保在MetaTrader 5中部署时,能够识别我们的工作。通过这种方式设置基本信息,我们为EA奠定了专业且文档完备的基础。

//+------------------------------------------------------------------+ 
//|                       Strategic Golden & Death Cross EA.mq5      |
//|                           Copyright 2024, Clemence Benjamin      |
//|        https://www.mql5.com/en/users/billionaire2024/seller      |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property description "GOLDEN AND DEATH CROSS"
#property version "1.0"

包含文件与交易对象初始化:

为简化交易执行流程,我们使用#include<Trade\Trade.mqh>引入MQL5交易库。该库提供了强大的交易功能接口。我们将CTrade类实例化为trade对象,使得EA能够直接调用买入、卖出和平仓等操作,而无需手动编写这些例程。这样既降低了代码复杂度,又提升了可靠性。通过此配置,我们可以专注于策略开发,并依赖交易管理库来执行订单。

#include<Trade\Trade.mqh>;
CTrade trade;

输入参数:

我们首先定义用户可调整的参数,以使EA具备灵活性和可定制性。

例如:

  • LotSize用于控制交易手数。
  • Slippage指定执行订单时可接受的价格偏差范围。
  • FastEMAPeriod和SlowEMAPeriod分别定义构成金叉与死叉策略核心的两条移动平均线的周期。
input double LotSize = 1.0;            // Trade volume (lots)
input int Slippage = 20;               // Slippage in points
input int TimerInterval = 10000;       // Timer interval in seconds
input double TakeProfitPips = 100;     // Take Profit in pips
input double StopLossPips = 50;        // Stop Loss in pips
input int FastEMAPeriod = 50;          // Fast EMA period (default 50)
input int SlowEMAPeriod = 200;         // Slow EMA period (default 200)

通过设置这些参数,我们允许用户根据自身特定的交易条件对EA进行调整。

初始化与定时器设置:

我们要确保EA能够正常初始化。在OnInit()函数中,我们添加一项检查,以验证是否存在足够的K线数据来计算慢速指数移动平均线(EMA)。如果数据不足,EA将记录错误日志并停止执行。这样就确保了策略仅在有足够数据可用时才会运行。

int OnInit()
{
   //--- create timer
   EventSetTimer(TimerInterval);

   //--- Check if there are enough bars for EMA calculation
   if(Bars(_Symbol,PERIOD_CURRENT)<SlowEMAPeriod)
   {
      Print("Not enough bars for EMA calculation");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

此外,我们使用EventSetTimer() 定期调用OnTimer() 函数,执行交易逻辑。OnDeinit()函数则确保在移除EA时停用定时器,从而释放系统资源。

void OnDeinit(const int reason)
{
   //--- delete timer
   EventKillTimer();
}

OnTimer()交易逻辑

现在,让我们深入探讨OnTimer()函数中的策略核心部分:

EMA计算:

我们首先使用iMA() 为快速和慢速EMA创建指标句柄,然后使用CopyBuffer()获取它们的值。这些EMA对于检测入场信号至关重要。

int fastEMAHandle = iMA(_Symbol, PERIOD_CURRENT, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
int slowEMAHandle = iMA(_Symbol, PERIOD_CURRENT, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

double fastEMAArray[], slowEMAArray[];
CopyBuffer(fastEMAHandle, 0, 0, 2, fastEMAArray);
CopyBuffer(slowEMAHandle, 0, 0, 2, slowEMAArray);

市场数据获取:

接下来,我们获取重要的市场数据,例如卖出价(Ask)、买入价(Bid)和点值(Point Size),以确保止损(Stop-loss)和止盈(Take-profit)计算的精确性。

double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

入场信号:

在此,我们定义开立多头(买入)和空头(卖出)交易的条件。金叉(快速EMA向上穿越慢速EMA)为买入信号,而死叉(快速EMA向下穿越慢速EMA)为卖出信号。

if(fastEMAArray[0] > slowEMAArray[0] && fastEMAArray[1] <= slowEMAArray[1]) // Death Cross 
{
   double sl = NormalizeDouble(ask + StopLossPips * point, _Digits);
   trade.Sell(LotSize, _Symbol, ask, sl);
}
else if(fastEMAArray[0] < slowEMAArray[0] && fastEMAArray[1] >= slowEMAArray[1]) // Golden Cross
{
   double sl = NormalizeDouble(bid - StopLossPips * point, _Digits);
   trade.Buy(LotSize, _Symbol, bid, sl);
}

离场信号:

我们要确保当出现相反信号时,平掉现有头寸。这能让策略与不断变化的市场状况保持一致。

if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && fastEMAArray[0] < slowEMAArray[0]) ||
   (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && fastEMAArray[0] > slowEMAArray[0]))
{
   trade.PositionClose(PositionGetInteger(POSITION_TICKET));
}

错误处理:

现在,我们添加错误处理程序,用于记录交易执行或平仓过程中出现的任何问题,这样有助于调试并确保系统平稳运行。

if(!trade.Sell(LotSize, _Symbol, ask, sl))
{
   Print("Sell order error: ", GetLastError());
}

以下是完整的代码:

//+------------------------------------------------------------------+ 
//|                          Golden & Death Cross Strategy.mq5       |
//|                           Copyright 2024, Clemence Benjamin      |
//|        https://www.mql5.com/en/users/billionaire2024/seller      |
//+------------------------------------------------------------------+

#property copyright "Clemence Benjamin"
#property description "GOLDEN AND DEATH CROSS"
#property version "1.0"


//+------------------------------------------------------------------+
//| Includes                                                         |
//+------------------------------------------------------------------+
#include<Trade\Trade.mqh>;
CTrade trade;

//+------------------------------------------------------------------+
//| Input parameters                                                 |
//+------------------------------------------------------------------+
input double LotSize = 1.0;            // Trade volume (lots)
input int Slippage = 20;               // Slippage in points
input int TimerInterval = 1000;          // Timer interval in seconds
input double StopLossPips = 1500;        // Stop Loss in pips
input int FastEMAPeriod = 50;          // Fast EMA period (default 50)
input int SlowEMAPeriod = 200;         // Slow EMA period (default 200)

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- create timer
   EventSetTimer(TimerInterval);
   
   //--- Check if there are enough bars to calculate the EMA
   if(Bars(_Symbol,PERIOD_CURRENT)<SlowEMAPeriod)
     {
      Print("Not enough bars for EMA calculation");
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- delete timer
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Expert timer function                                            |
//+------------------------------------------------------------------+
void OnTimer()
{
   bool hasPosition = PositionSelect(_Symbol);
   
   int fastEMAHandle = iMA(_Symbol, PERIOD_CURRENT, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   int slowEMAHandle = iMA(_Symbol, PERIOD_CURRENT, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

   if(fastEMAHandle < 0 || slowEMAHandle < 0)
   {
      Print("Failed to create EMA handles. Error: ", GetLastError());
      return;
   }

   double fastEMAArray[], slowEMAArray[];
   if(CopyBuffer(fastEMAHandle, 0, 0, 2, fastEMAArray) <= 0 ||
      CopyBuffer(slowEMAHandle, 0, 0, 2, slowEMAArray) <= 0)
   {
      Print("Failed to copy EMA data. Error: ", GetLastError());
      return;
   }

   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   if(!hasPosition)
   {
      if(fastEMAArray[0] > slowEMAArray[0] && fastEMAArray[1] <= slowEMAArray[1]) // Death Cross 
      {
         
         double sl = NormalizeDouble(ask + StopLossPips * point, _Digits);
         if(!trade.Sell(LotSize, _Symbol, ask, sl ))
            Print("Buy order error ", GetLastError());
         else
            Print("Buy order opened with TP ", " and SL ", StopLossPips, " pips");
      }
      else if(fastEMAArray[0] < slowEMAArray[0] && fastEMAArray[1] >= slowEMAArray[1]) // Golden Cross
      {
         
         double sl = NormalizeDouble(bid - StopLossPips * point, _Digits);
         if(!trade.Buy(LotSize, _Symbol, bid, sl ))
            Print("Sell order error ", GetLastError());
         else
            Print("Sell order opened with TP ",  " and SL ", StopLossPips, " pips");
      }
   }
   else
   {
     if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && fastEMAArray[0] < slowEMAArray[0]) ||
         (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && fastEMAArray[0] > slowEMAArray[0]))
    {
         ulong ticket = PositionGetInteger(POSITION_TICKET);
         if(!trade.PositionClose(ticket))
            Print("Failed to close position (Ticket: ", ticket, "). Error: ", GetLastError());
         else  
            Print("Position closed : ", ticket);
      }
   }
}

//+------------------------------------------------------------------+


初始策略测试

代码成功编译后,我们现在即可使用策略测试器(Strategy Tester)来测试策略的性能。以下是在将该策略集成至趋势约束型EA之前,策略测试结果的图片。

策略测试器设置.PNG

策略测试器设置:Boom 500指数

输入设置

输入设置:Boom 500指数

Boom 500指数M5:金叉与死叉策略测试

从初步测试结果来看,订单执行逻辑运行顺畅,但离场策略有待改进。在策略测试器中进行人工观察时,我发现许多本可获利结单的头寸,最终却仅以微利甚至亏损平仓。这是因为反转行情在出现大幅回撤后才呈现交叉信号,限制了盈利空间。在市场盘整阶段,众多虚假交叉信号又导致了亏损。为避免此类大幅回撤,离场策略如仍需优化。


将新策略融入趋势约束中

最终,我们通过整合此前开发的策略,成功实现了多策略EA的构建目标。如需回顾现有策略,可参阅(第九部分)。以下是已整合的策略列表:

  • 趋势跟踪策略
  • 唐奇安通道突破策略
  • 背离策略

今日,我们将添加第四项策略。正如前文所述,该策略独立运行且不受约束条件限制,能够捕捉所有反转机会。为此,我们将对现有趋势约束型EA进行修改,为新增的金叉与死叉策略增设布尔开关参数。此外,我们将重构其他代码模块,将其整合至主程序的相关函数中。

为避免与主程序中现有变量命名冲突,我们为金叉与死叉策略相关变量添加了唯一前缀。例如,将LotSize重命名为GDC_LotSize = 1.0,以确保命名清晰并防止混淆。

以下是完整且正确的代码。新增内容与修改部分均已明确标注,便于理解与查阅。

//+------------------------------------------------------------------+
//|                                      Trend Constraint Expert.mq5 |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamini"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.03"

#include <Trade\Trade.mqh>
CTrade trade;

// Input parameters for controlling strategies
input bool UseTrendFollowingStrategy = false;   // Enable/Disable Trend Following Strategy
input bool UseBreakoutStrategy = false;         // Enable/Disable Breakout Strategy
input bool UseDivergenceStrategy = false;       // Enable/Disable Divergence Strategy
input bool UseGoldenDeathCrossStrategy = true;  // Enable/Disable Golden/Death Cross Strategy

// Input parameters for Golden/Death Cross Strategy
input double GDC_LotSize = 1.0;            // Trade volume (lots) for Golden Death Cross
input int GDC_Slippage = 20;               // Slippage in points for Golden Death Cross
input int GDC_TimerInterval = 1000;        // Timer interval in seconds for Golden Death Cross
input double GDC_StopLossPips = 1500;      // Stop Loss in pips for Golden Death Cross
input int GDC_FastEMAPeriod = 50;          // Fast EMA period for Golden Death Cross
input int GDC_SlowEMAPeriod = 200;         // Slow EMA period for Golden Death Cross

int GDC_fastEMAHandle, GDC_slowEMAHandle;  // Handles for EMA indicators in Golden Death Cross

// Global variables
double prevShortMA, prevLongMA;

// Input parameters for Trend Constraint Strategy
input int    RSI_Period = 14;            // RSI period
input double RSI_Overbought = 70.0;     // RSI overbought level
input double RSI_Oversold = 30.0;       // RSI oversold level
input double Lots = 0.1;                // Lot size
input double StopLoss = 100;            // Stop Loss in points
input double TakeProfit = 200;          // Take Profit in points
input double TrailingStop = 50;         // Trailing Stop in points
input int    MagicNumber = 12345678;    // Magic number for the Trend Constraint EA
input int    OrderLifetime = 43200;     // Order lifetime in seconds (12 hours)

// Input parameters for Breakout Strategy
input int InpDonchianPeriod = 20;       // Period for Donchian Channel
input double RiskRewardRatio = 1.5;     // Risk-to-reward ratio
input double LotSize = 0.1;             // Default lot size for trading
input double pipsToStopLoss = 15;       // Stop loss in pips for Breakout
input double pipsToTakeProfit = 30;     // Take profit in pips for Breakout

// Input parameters for Divergence Strategy
input int DivergenceMACDPeriod = 12;    // MACD Fast EMA period
input int DivergenceSignalPeriod = 9;   // MACD Signal period
input double DivergenceLots = 1.0;      // Lot size for Divergence trades
input double DivergenceStopLoss = 300;   // Stop Loss in points for Divergence
input double DivergenceTakeProfit = 500; // Take Profit in points for Divergence
input int DivergenceMagicNumber = 87654321;     // Magic number for Divergence Strategy
input int DivergenceLookBack = 8;       // Number of periods to look back for divergence
input double profitLockerPoints  = 20;  // Number of profit points to lock

// Indicator handle storage
int rsi_handle;                         
int handle;                             // Handle for Donchian Channel
int macd_handle;

double ExtUpBuffer[];                   // Upper Donchian buffer
double ExtDnBuffer[];                   // Lower Donchian buffer
double ExtMacdBuffer[];                 // MACD buffer
double ExtSignalBuffer[];               // Signal buffer
int globalMagicNumber;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    prevShortMA = 0.0;
    prevLongMA = 0.0;
    // Initialize RSI handle
    rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
    if (rsi_handle == INVALID_HANDLE)
    {
        Print("Failed to create RSI indicator handle. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Create a handle for the Donchian Channel
    handle = iCustom(_Symbol, PERIOD_CURRENT, "Free Indicators\\Donchian Channel", InpDonchianPeriod);
    if (handle == INVALID_HANDLE)
    {
        Print("Failed to load the Donchian Channel indicator. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Initialize MACD handle for divergence
    globalMagicNumber = DivergenceMagicNumber;
    macd_handle = iMACD(_Symbol, PERIOD_CURRENT, DivergenceMACDPeriod, 26, DivergenceSignalPeriod, PRICE_CLOSE);
    if (macd_handle == INVALID_HANDLE)
    {
        Print("Failed to create MACD indicator handle for divergence strategy. Error: ", GetLastError());
        return INIT_FAILED;
    }
    
    
    if(UseGoldenDeathCrossStrategy)
{
    // Check if there are enough bars to calculate the EMA
    if(Bars(_Symbol, PERIOD_CURRENT) < GDC_SlowEMAPeriod)
    {
        Print("Not enough bars for EMA calculation for Golden Death Cross");
        return INIT_FAILED;
    }
    GDC_fastEMAHandle = iMA(_Symbol, PERIOD_CURRENT, GDC_FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
    GDC_slowEMAHandle = iMA(_Symbol, PERIOD_CURRENT, GDC_SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
    if(GDC_fastEMAHandle < 0 || GDC_slowEMAHandle < 0)
    {
        Print("Failed to create EMA handles for Golden Death Cross. Error: ", GetLastError());
        return INIT_FAILED;
    }
}

    // Resize arrays for MACD buffers
    ArrayResize(ExtMacdBuffer, DivergenceLookBack);
    ArrayResize(ExtSignalBuffer, DivergenceLookBack);

    Print("Trend Constraint Expert initialized.");
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    IndicatorRelease(rsi_handle);
    IndicatorRelease(handle);
    IndicatorRelease(macd_handle);
    Print("Trend Constraint Expert deinitialized.");
}

//+------------------------------------------------------------------+
//| Check Golden/Death Cross Trading Logic                           |
//+------------------------------------------------------------------+
void CheckGoldenDeathCross()
{
    double fastEMAArray[2], slowEMAArray[2];
    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

    if(CopyBuffer(GDC_fastEMAHandle, 0, 0, 2, fastEMAArray) <= 0 ||
       CopyBuffer(GDC_slowEMAHandle, 0, 0, 2, slowEMAArray) <= 0)
    {
        Print("Failed to copy EMA data for Golden Death Cross. Error: ", GetLastError());
        return;
    }

    bool hasPosition = PositionSelect(_Symbol);

    if(!hasPosition)
    {
        if(fastEMAArray[0] > slowEMAArray[0] && fastEMAArray[1] <= slowEMAArray[1]) // Death Cross 
        {
            double sl = NormalizeDouble(ask + GDC_StopLossPips * point, _Digits);
            if(!trade.Sell(GDC_LotSize, _Symbol, ask, sl ))
                Print("Sell order error for Golden Death Cross ", GetLastError());
            else
                Print("Sell order opened for Golden Death Cross with SL ", GDC_StopLossPips, " pips");
        }
        else if(fastEMAArray[0] < slowEMAArray[0] && fastEMAArray[1] >= slowEMAArray[1]) // Golden Cross
        {
            double sl = NormalizeDouble(bid - GDC_StopLossPips * point, _Digits);
            if(!trade.Buy(GDC_LotSize, _Symbol, bid, sl ))
                Print("Buy order error for Golden Death Cross ", GetLastError());
            else
                Print("Buy order opened for Golden Death Cross with SL ", GDC_StopLossPips, " pips");
        }
    }
    else
    {
        if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && fastEMAArray[0] < slowEMAArray[0]) ||
           (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && fastEMAArray[0] > slowEMAArray[0]))
        {
            ulong ticket = PositionGetInteger(POSITION_TICKET);
            if(!trade.PositionClose(ticket))
                Print("Failed to close position for Golden Death Cross (Ticket: ", ticket, "). Error: ", GetLastError());
            else  
                Print("Position closed for Golden Death Cross: ", ticket);
        }
    }
}

//+------------------------------------------------------------------+
//| Check Trend Following Strategy                                   |
//+------------------------------------------------------------------+
void CheckTrendFollowing()
{
   if (PositionsTotal() >= 2) return; // Ensure no more than 2 orders from this strategy

    double rsi_value;
    double rsi_values[];
    if (CopyBuffer(rsi_handle, 0, 0, 1, rsi_values) <= 0)
    {
        Print("Failed to get RSI value. Error: ", GetLastError());
        return;
    }
    rsi_value = rsi_values[0];

    double ma_short = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
    double ma_long = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_EMA, PRICE_CLOSE);

    bool is_uptrend = ma_short > ma_long;
    bool is_downtrend = ma_short < ma_long;

    if (is_uptrend && rsi_value < RSI_Oversold)
    {
        double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double stopLossPrice = currentPrice - StopLoss * _Point;
        double takeProfitPrice = currentPrice + TakeProfit * _Point;

        // Corrected Buy method call with 6 parameters
        if (trade.Buy(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Buy"))
        {
            Print("Trend Following Buy order placed.");
        }
    }
    else if (is_downtrend && rsi_value > RSI_Overbought)
    {
        double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double stopLossPrice = currentPrice + StopLoss * _Point;
        double takeProfitPrice = currentPrice - TakeProfit * _Point;

        // Corrected Sell method call with 6 parameters
        if (trade.Sell(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Sell"))
        {
            Print("Trend Following Sell order placed.");
        }
    }
}

//+------------------------------------------------------------------+
//| Check Breakout Strategy                                          |
//+------------------------------------------------------------------+
void CheckBreakoutTrading()
{
    if (PositionsTotal() >= 2) return; // Ensure no more than 2 orders from this strategy

    ArrayResize(ExtUpBuffer, 2);
    ArrayResize(ExtDnBuffer, 2);

    if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0)
    {
        Print("Error reading Donchian Channel buffer. Error: ", GetLastError());
        return;
    }

    double closePrice = iClose(_Symbol, PERIOD_CURRENT, 0);
    double lastOpen = iOpen(_Symbol, PERIOD_D1, 1);
    double lastClose = iClose(_Symbol, PERIOD_D1, 1);

    bool isBullishDay = lastClose > lastOpen;
    bool isBearishDay = lastClose < lastOpen;

    if (isBullishDay && closePrice > ExtUpBuffer[1])
    {
        double stopLoss = closePrice - pipsToStopLoss * _Point;
        double takeProfit = closePrice + pipsToTakeProfit * _Point;
        if (trade.Buy(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Buy") > 0)
        {
            Print("Breakout Buy order placed.");
        }
    }
    else if (isBearishDay && closePrice < ExtDnBuffer[1])
    {
        double stopLoss = closePrice + pipsToStopLoss * _Point;
        double takeProfit = closePrice - pipsToTakeProfit * _Point;
        if (trade.Sell(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Sell") > 0)
        {
            Print("Breakout Sell order placed.");
        }
    }
}

//+------------------------------------------------------------------+
//| Check Divergence Trading                                         |
//+------------------------------------------------------------------+
bool CheckBullishRegularDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceLow1 < priceLow2 && macdLow1 > macdLow2);
}

bool CheckBullishHiddenDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceLow1 > priceLow2 && macdLow1 < macdLow2);
}

bool CheckBearishRegularDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2];
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceHigh1 > priceHigh2 && macdHigh1 < macdHigh2);
}

bool CheckBearishHiddenDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2]; 
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceHigh1 < priceHigh2 && macdHigh1 > macdHigh2);
}

void CheckDivergenceTrading()
{
    if (!UseDivergenceStrategy) return;

    // Check if no position is open or if less than 3 positions are open
    int openDivergencePositions = CountOrdersByMagic(DivergenceMagicNumber);
    if (openDivergencePositions == 0 || openDivergencePositions < 3)
    {
        int barsAvailable = Bars(_Symbol, PERIOD_CURRENT);
        if (barsAvailable < DivergenceLookBack * 2)
        {
            Print("Not enough data bars for MACD calculation.");
            return;
        }

        int attempt = 0;
        while(attempt < 6)
        {
            if (CopyBuffer(macd_handle, 0, 0, DivergenceLookBack, ExtMacdBuffer) > 0 &&
                CopyBuffer(macd_handle, 1, 0, DivergenceLookBack, ExtSignalBuffer) > 0)
                break; 
            
            Print("Failed to copy MACD buffer, retrying...");
            Sleep(1000);
            attempt++;
        }
        if(attempt == 6)
        {
            Print("Failed to copy MACD buffers after ", attempt, " attempts.");
            return;
        }

        if(TimeCurrent() == iTime(_Symbol, PERIOD_CURRENT, 0))
        {
            Print("Skipping trade due to incomplete bar data.");
            return;
        }

        double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0);
        double dailyClose = iClose(_Symbol, PERIOD_D1, 0);
        double dailyOpen = iOpen(_Symbol, PERIOD_D1, 0);
        bool isDailyBullish = dailyClose > dailyOpen;
        bool isDailyBearish = dailyClose < dailyOpen;

        // Only proceed with buy orders if D1 is bullish
        if (isDailyBullish)
        {
            if ((CheckBullishRegularDivergence() && ExtMacdBuffer[0] > ExtSignalBuffer[0]) ||
                CheckBullishHiddenDivergence())
            {
                ExecuteDivergenceOrder(true);
            }
        }

        // Only proceed with sell orders if D1 is bearish
        if (isDailyBearish)
        {
            if ((CheckBearishRegularDivergence() && ExtMacdBuffer[0] < ExtSignalBuffer[0]) ||
                CheckBearishHiddenDivergence())
            {
                ExecuteDivergenceOrder(false);
            }
        }
    }
    else
    {
        Print("Divergence strategy: Maximum number of positions reached.");
    }
}

void ExecuteDivergenceOrder(bool isBuy)
{
    // Ensure the magic number is set for the trade
    trade.SetExpertMagicNumber(DivergenceMagicNumber);
    
    double currentPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double stopLossPrice = isBuy ? currentPrice - DivergenceStopLoss * _Point : currentPrice + DivergenceStopLoss * _Point;
    double takeProfitPrice = isBuy ? currentPrice + DivergenceTakeProfit * _Point : currentPrice - DivergenceTakeProfit * _Point;

    if (isBuy)
    {
        if (trade.Buy(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Buy"))
        {
            Print("Divergence Buy order placed.");
        }
    }
    else
    {
        if (trade.Sell(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Sell"))
        {
            Print("Divergence Sell order placed.");
        }
    }
}

int CountOrdersByMagic(int magic)
{
    int count = 0;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            if (PositionGetInteger(POSITION_MAGIC) == magic)
            {
                count++;
            }
        }
    }
    return count;
}

//+------------------------------------------------------------------+
//| Profit Locking Logic                                             |
//+------------------------------------------------------------------+
void LockProfits()
{
    for (int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            double currentProfit = PositionGetDouble(POSITION_PROFIT);
            double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
            
            // Convert profit to points
            double profitPoints = MathAbs(currentProfit / _Point);

            // Check if profit has exceeded 100 points
            if (profitPoints >= 100)
            {
                double newStopLoss;
                
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    newStopLoss = entryPrice + profitLockerPoints * _Point; // 20 points above entry for buys
                }
                else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
                {
                    newStopLoss = entryPrice - profitLockerPoints * _Point; // 20 points below entry for sells
                }
                else
                {
                    continue; // Skip if not a buy or sell position
                }

                // Modify stop loss only if the new stop loss is more protective
                double currentStopLoss = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    if (currentStopLoss < newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for buy position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
                else // POSITION_TYPE_SELL
                {
                    if (currentStopLoss > newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for sell position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
            }
        }
    }
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    if (UseTrendFollowingStrategy)
        CheckTrendFollowing();
    if (UseBreakoutStrategy)
        CheckBreakoutTrading();
    if (UseDivergenceStrategy)
        CheckDivergenceTrading();
    if(UseGoldenDeathCrossStrategy)
        CheckGoldenDeathCross();

}


测试和优化结果

启动趋势约束型EA。

趋势约束型EA:以默认设置添加至图表

策略可视化工具

在策略测试器中,将金叉与死叉策略作为其他策略的组成部分进行可视化展示


结论

我们确实能够持续扩展EA的代码,但这样做会使代码愈发复杂,且处理时对系统资源的要求也会不断提高。这凸显了改进资源管理技术的必要性。将人工智能(AI)模型融入这些理念中尤为有益,因为它们能够有效应对这些复杂状况。在本项目中,我们成功地在趋势约束型EA中整合了一种最受欢迎的捕捉反转机会的策略——金叉与死叉策略。

第一部分至今,我们所涵盖的基础阶段为这款EA奠定了基石。然而,要实现最优效果,还需通过优化各类参数设置并调整特定结构特性来完善该EA模型。这种做法使其成为极具价值的教学与实验工具。请注意,本EA并不保证盈利,仅供教学与研究之用。

在后续的章节中,我计划进一步优化现有策略,并引入机器学习技术以提升EA的功能与性能。

返回概述

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

附加的文件 |
最近评论 | 前往讨论 (1)
Christian Paul Anasco
Christian Paul Anasco | 20 12月 2024 在 03:15
这正是我喜欢的。代码非常简洁。易于阅读和理解。
交易策略 交易策略
各种交易策略的分类都是任意的,下面这种分类强调从交易的基本概念上分类。
开发先进的 ICT 交易系统:在指标中实现订单区块 开发先进的 ICT 交易系统:在指标中实现订单区块
在本文中,我们将学习如何创建一个指标来检测、绘制订单区块并提醒订单块的缓解。我们还将详细研究如何在图表上识别这些区块,设置准确的提醒,并使用矩形可视化它们的位置,以更好地了解价格行为。该指标将成为遵循聪明钱概念和内圈交易者(ICT,Inner Circle Trader)方法的交易者的关键工具。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
将 Discord 与 MetaTrader 5 集成:构建具有实时通知功能的交易机器人 将 Discord 与 MetaTrader 5 集成:构建具有实时通知功能的交易机器人
本文将介绍如何将 MetaTrader 5 与 Discord 服务器集成,以便能从任何地方实时接收交易通知。我们将了解如何配置平台和 Discord,以启用向 Discord 发送警报的功能。我们还将讨论在使用 WebRequests 和 webhook 实现此类警报解决方案时可能引发的安全问题。