构建K线趋势约束模型(第九部分):多策略智能交易系统(EA)(三)
概述
在算法交易中,于既定趋势中精准捕捉最优入场点始终是一大挑战。许多策略因难以把握时机或频繁产生虚假信号,导致交易表现不佳。这一问题在日线趋势中尤为突出——微小波动可能严重干扰执行精度。
背离策略通过检测价格走势与动量指标之间的差异,提供了一种强效的过滤手段,可识别潜在反转或延续信号。将背离检测功能集成至趋势约束智能交易系统中,交易者能显著提升入场点定位的精准度。
该方法不仅优化了交易准确性,更结合了MQL5的强大功能,确保交易策略的稳定高效执行。在本文中,我们将探讨背离策略的基本原理,以及将其集成至MQL5智能交易系统的具体步骤,介绍通过新增交易执行条件对趋势约束智能交易系统进行优化升级,并通过回测结果展示其实际应用效果。
核心内容:
背离策略的基础原理
背离是技术分析中的核心概念,通过对比价格走势与指标方向,为交易者揭示潜在的价格反转或延续信号。
背离的意义:
当资产价格走势与某一技术指标方向相反时,即形成背离,通常预示趋势减弱或即将反转。这一概念在识别趋势可能回调或彻底转向的时机时尤为有效。
背离的类型:
- 看涨背离:当资产价格创出新低,但指标(如RSI)显示更高低点时,表明下行趋势减弱。这可能意味着卖压正在消退,价格有望向上反弹。
- 看跌背离:当价格创出新高,但指标显示更低高点时,表明上行趋势减弱。这可能预示价格即将下跌。
背景核查:
背离是技术分析中的关键概念,深刻影响着市场行为与交易者策略。Bart和Masse(1981)在《意见分歧与风险》(Divergence of Opinion and Risk)一文中强调,市场观点的差异会加剧风险与价格波动,而这一观点恰恰印证了背离在技术分析中的核心作用。
Tilehnouei和Shivaraj(2013)的实证研究表明,在特定情境下,MACD等工具通过背离信号分析市场动能的表现可能优于RSI。这一研究揭示了不同指标在捕捉背离现象时的差异化优势。基于上述研究,结合RSI、MACD与价格行为等指标的交互验证,可显著强化背离策略在系统性交易框架中的实用性——这一结论亦得到行业多源数据支持。
下一部分,我们将进入实战环节,探讨如何在EA开发中具体实现背离策略。
集成背离检测的步骤
要将背离检测功能集成至MQL5 EA中,我们首先需通过iRSI()等函数计算相对强弱指数(RSI)值,并将其与价格走势进行对比分析。通过iHigh()和iLow()函数在特定周期内识别价格极值点,为后续背离判断提供基础数据。在本项目中,我将背离分为两类:常规背离(反转型)与隐藏背离(延续型)。
常规背离:
常规背离用于提示潜在趋势反转,当价格创出新低而指标形成更高低点时,形成看涨形态;当价格创出新高而指标形成更低高点时,形成看跌形态。
// Regular divergence conditions in code bool CheckRegularBearishDivergence(int period = 14, ENUM_TIMEFRAMES timeframe = PERIOD_H1) { double priceHigh1 = iHigh(_Symbol, timeframe, 2); double priceHigh2 = iHigh(_Symbol, timeframe, 8); double rsiHigh1 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 2); double rsiHigh2 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 8); if(priceHigh1 > priceHigh2 && rsiHigh1 < rsiHigh2) return true; return false; }
为了直观地呈现常规背离,以下两张图分别展示了看涨背离与看跌背离:

Boom 300指数H4看涨背离:价格创出新低的B点,而RSI指标在D处出现更高的低点

Boom 300指数H4看跌背离:价格形成更高的B点,而RSI在D处却出现更低的高点
隐藏背离
另一方面,隐藏背离则暗示趋势延续。在上升趋势中,若价格创出更高的低点,而指标却出现更低的低点,则形成隐藏看涨背离;在下降趋势中,若价格创出更低的高点,而指标却出现更高的高点,则形成隐藏看跌背离。
//RSI and Price Levels declaration and hidden divergence condition bool CheckHiddenBullishDivergence(int period = 14, ENUM_TIMEFRAMES timeframe = PERIOD_H1) { double priceLow1 = iLow(_Symbol, timeframe, 2); double priceLow2 = iLow(_Symbol, timeframe, 8); double rsiLow1 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 2); double rsiLow2 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 8); if(priceLow1 > priceLow2 && rsiLow1 < rsiLow2) return true; return false; }
以下图片展示了看涨隐藏背离与看跌隐藏背离的形态。请务必对照前文描述,并在自己的行情图中练习识别同类模式。

Boom 300指数H4:看涨隐藏背离

Boom 300指数H4:看跌隐藏背离
将上述背离类型集成至EA需通过代码实现在每个tick或K线收盘时检测背离条件,并利用iRSI()或iMACD()等函数计算指标值。检测到背离信号后,需结合每日市场情绪识别的趋势约束条件进行信号过滤。
增强型趋势约束智能交易系统:引入基于背离的新交易执行条件
关于上述环节,当背离信号最终被确认时,需引入辅助指标作为订单执行的二次验证工具。可选指标众多,本项目将采用MACD与RSI组合验证。其他可选确认指标包括:
- 布林带
- 随机振荡器
- 能量潮(OBV)和成交量加权平均价(VWAP)
- 平均动向指数(ADX)
移动平均收敛发散指标(MACD)说明:
选择原因:MACD可验证动能变化。当RSI检测到背离时,MACD可通过趋势强度或衰减的二次确认,提升信号的可靠性。
其工作原理如下:
EA将监测与背离信号同步的MACD线交叉或柱状图变化。例如,若出现看跌背离,同时MACD线跌破信号线或柱状图开始收缩,则确认信号有效。
MACD指标核心特性:
如需预览MetaEditor 5内置的MACD等指标代码,可进入软件安装目录下的Examples/Indicators文件夹,请参考下图说明:

MetaEditor 5:访问MACD指标源文件
访问源码的目的是为了快速理解指标缓冲区(Buffers)的设计逻辑,从而便于在自定义EA中无缝调用和适配这些数据。以下为MACD指标中我们关注的缓冲区声明代码段。
//--- indicator buffers double ExtMacdBuffer[]; double ExtSignalBuffer[]; double ExtFastMaBuffer[]; double ExtSlowMaBuffer[]; int ExtFastMaHandle; int ExtSlowMaHandle;
现在,请注意缓冲区,让我们按以下步骤逐步开发。
背离策略开发:
步骤1:参数声明与输入配置
首先,为背离策略声明输入参数,设置MACD缓冲区,并初始化CTrade类。
#include <Trade\Trade.mqh> CTrade trade; input bool UseDivergenceStrategy = true; // Enable/Disable 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 double ExtMacdBuffer[]; // MACD values double ExtSignalBuffer[]; // Signal line values int macd_handle; // MACD indicator handle
步骤2:初始化MACD指标
在OnInit()函数中初始化MACD指标句柄并且分配缓冲区内存。
int OnInit() { macd_handle = iMACD(_Symbol, PERIOD_CURRENT, DivergenceMACDPeriod, 26, DivergenceSignalPeriod, PRICE_CLOSE); if (macd_handle == INVALID_HANDLE) { Print("Failed to initialize MACD. Error: ", GetLastError()); return INIT_FAILED; } ArrayResize(ExtMacdBuffer, DivergenceLookBack); ArrayResize(ExtSignalBuffer, DivergenceLookBack); return INIT_SUCCEEDED; }
步骤3:背离信号检测
当资产价格走势与指标(本例中为MACD)出现方向性分歧时,即形成背离信号。本策略可识别以下四种背离类型:常规看涨背离、隐藏看涨背离、常规看跌背离和隐藏看跌背离。每种背离类型均需满足特定的条件,即通过对比价格的高点或低点与对应MACD指标的高点或低点,以判断背离信号是否存在。
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 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); }
步骤4:交易逻辑
首先,该策略会确认背离交易已启用,且当前基于背离信号的开仓数不超过三笔。它会获取MACD缓冲区数据,一旦获取失败会重试,并仅在完整K线上执行交易。此外,策略还将交易与日线K线趋势对齐,确保日线看涨时只做多,日线看跌时只做空。
void CheckDivergenceTrading()
{
if (!UseDivergenceStrategy) return;
int openDivergencePositions = CountOrdersByMagic(DivergenceMagicNumber);
if (openDivergencePositions == 0 || openDivergencePositions < 3)
{
if (CopyBuffer(macd_handle, 0, 0, DivergenceLookBack, ExtMacdBuffer) > 0 &&
CopyBuffer(macd_handle, 1, 0, DivergenceLookBack, ExtSignalBuffer) > 0)
{
double dailyClose = iClose(_Symbol, PERIOD_D1, 0);
double dailyOpen = iOpen(_Symbol, PERIOD_D1, 0);
bool isDailyBullish = dailyClose > dailyOpen;
bool isDailyBearish = dailyClose < dailyOpen;
if (isDailyBullish &&
(CheckBullishRegularDivergence() && ExtMacdBuffer[0] > ExtSignalBuffer[0]) ||
CheckBullishHiddenDivergence())
{
ExecuteDivergenceOrder(true);
}
if (isDailyBearish &&
(CheckBearishRegularDivergence() && ExtMacdBuffer[0] < ExtSignalBuffer[0]) ||
CheckBearishHiddenDivergence())
{
ExecuteDivergenceOrder(false);
}
}
}
}
步骤5:下单执行
一旦检测到背离,策略即用预设的“手数、止损、止盈”等参数执行交易。ExecuteDivergenceOrder函数会根据交易方向计算合适的价位,并通过交易对象下达买单或卖单。
void ExecuteDivergenceOrder(bool isBuy) { 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."); } }
步骤6:订单管理
为防止过度交易,策略使用CountOrdersByMagic工具函数统计所有带指定magic编号的持仓数量。这样可以确保背离类交易不超过设定的最大持仓上限。
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; }
magic编号:
另一个关键要点是为我们的持仓赋予唯一身份。在此,我们为背离策略所管理的所有交易分配了一个唯一的magic编号。
// Ensure the magic number is set for the trade trade.SetExpertMagicNumber(DivergenceMagicNumber);
步骤7:盈利保护
我们为趋势约束型智能交易系统引入了盈利保护功能,该功能集成动态利润锁定逻辑,可实时保障已开仓位的盈利安全。LockProfits函数会扫描所有活跃持仓,识别盈利超过100点阈值的仓位。针对每个符合条件的仓位,系统根据profitLockerPoints参数(例如:距入场价20点)计算新的止损价位。
此调整将止损位向当前价格方向移动,实现盈利锁定效果。对于买单,止损位被上移至入场价上方;对于卖单,止损位则被下移至入场价下方。函数仅在新止损位能提供更优保护时更新,确保风险控制最优化。成功修改后会记录日志以便追踪。该功能在保障既有利润的同时,为仓位保留进一步盈利空间。
以下是利润锁定函数代码:
//+------------------------------------------------------------------+ //| 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); } } } } } } }
步骤8:集成至OnTick()主循环
在EA的核心循环中调用背离交易逻辑模块。
void OnTick() { CheckDivergenceTrading(); }
步骤9:系统关闭处理
void OnDeinit(const int reason) { IndicatorRelease(macd_handle); }
将该策略集成到主趋势约束智能交易系统。
从本系列上一篇文章中可知,我们已经开发了一个基于唐奇安通道的EA,该EA同时包含两种策略。今天,我们引入第3种策略,并用布尔变量来控制各策略的启用和禁用。
// Input parameters for controlling strategies input bool UseTrendFollowingStrategy = false; // Enable/Disable Trend Constraint Strategy input bool UseBreakoutStrategy = false; // Enable/Disable Breakout Strategy input bool UseDivergenceStrategy = true; // Enable/Disable Divergence Strategy
我们将背离策略的启用状态设置为true,以避免在策略测试过程中产生混淆。
最终,我们需将策略严谨地整合至主程序的代码中:
//+------------------------------------------------------------------+ //| Trend Constraint Expert.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.02" #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 = true; // Enable/Disable Divergence Strategy // 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() { // 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; } // Resize arrays for MACD buffers ArrayResize(ExtMacdBuffer, DivergenceLookBack); ArrayResize(ExtSignalBuffer, DivergenceLookBack); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(rsi_handle); IndicatorRelease(handle); IndicatorRelease(macd_handle); } //+------------------------------------------------------------------+ //| Check and execute Trend Following EA trading logic | //+------------------------------------------------------------------+ 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 and execute Breakout EA trading logic | //+------------------------------------------------------------------+ 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."); } } } //+------------------------------------------------------------------+ //| DIVERGENCE TRADING STRATEGY | //+------------------------------------------------------------------+ 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(); LockProfits(); // Call this function to check and lock profits }
回测结果与实战应用
要进行测试,需在交易终端的“Experts”栏目下找到 趋势约束型智能交易系统。请您务必确保在模拟账户测试运行。打开策略测试器(Strategy Tester)窗口后,可通过调整输入参数实现不同场景的优化。下图展示了默认参数设置下的界面示例:
趋势约束型智能交易系统:模拟账户参数配置
我们在策略测试器中运行了回测,所有交易均成功执行。系统将每轮交易中的最大持仓数量限制为三笔,有效避免了无节制的订单堆积问题。下图展示了回测过程中的部分交易记录:

趋势约束型智能交易系统:欧元兑美元(EURUSD)M15时间框架测试
下图表明,我们的持仓管理功能正按预期运行。根据实现逻辑,最多仅保留三笔订单,每笔均带有“Divergence Sell”注释,易于识别且完全符合策略设定。
趋势约束型智能交易系统:每次最大持仓数量为三笔
结论
我们深入地探讨了多种类型的背离形态,并将其以代码形式整合至一套协同策略中——该策略融合了RSI、MACD等指标,以生成精准的交易信号。为进一步提升信号的可靠性,我们引入了日线级K线趋势约束条件,确保所有交易信号均与更广泛的市场趋势保持一致。当前,我们的趋势约束型智能交易系统已具备三大独立且可配置的交易策略模块,用户可根据自身偏好及市场环境灵活调整参数组合。
在交易管理优化方面,我们为每笔订单分配了唯一标识符(MAGIC编号),实现了对持仓订单的精准管控,并限制了单策略的最大持仓数量。此外,我们开发了自定义的利润锁定功能:当市场在触及止盈目标前出现反转时,系统将动态调整止损价位,确保已获利润不被侵蚀。这一设计兼顾了风险控制与策略灵活性,使EA具备更强的稳健性与适应性。下方附上EA的完整源代码文件。欢迎下载使用、测试不同参数配置,并在评论区分享您的使用反馈。重要提示:本示例仅供教学用途,所有测试务必在模拟账户中进行。
祝各位交易者开发顺利!
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/16549
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
您应当知道的 MQL5 向导技术(第 50 部分):动量振荡器
开发多币种 EA 交易(第 19 部分):创建用 Python 实现的阶段
构建MQL5自优化智能交易系统(第二部分):美元兑日元(USDJPY)剥头皮策略
您应当知道的 MQL5 向导技术(第 49 部分):搭配近端政策优化的强化学习