
Построение модели для ограничения диапазона сигналов по тренду (Часть 9): Советник с несколькими стратегиями (III)
Введение
В алгоритмической торговле определение оптимальных точек входа в рамках преобладающего тренда остается серьезной проблемой, поскольку многие стратегии не могут уловить нужный момент или часто генерируют ложные сигналы, что приводит к неоптимальным результатам. Эта проблема особенно ярко проявляется в дневных трендах, где незначительные колебания могут нарушить точность исполнения.
Дивергенция предлагает надежное решение, выступая в качестве фильтра для выявления потенциальных разворотов или продолжений посредством расхождений между движениями цен и индикаторами импульса. Интегрировав функцию обнаружения дивергенции в советник Trend Constraint, трейдеры могут значительно повысить точность определения уровней входа.
Такой подход повышает точность торговли, а также обеспечивает последовательную и эффективную торговлю в сочетании с расширенными возможностями MQL5. В этой статье мы рассмотрим основы дивергенции, шаги по ее интеграции в советники MQL5, усовершенствования советника Trend Constraint с новыми условиями исполнения сделок, а также выводы из тестирования на истории, чтобы продемонстрировать ее практическое применение.
Содержание:
- Основы дивергенции
- Шаги по интеграции обнаружения расхождений
- Улучшения советника Trend Constraint: Реализация новых условий исполнения сделок для использования дивергенции.
- Результаты тестирования на истории и практическое применение
- Заключение
Основы дивергенции
Дивергенция — ключевое понятие в техническом анализе, дающее трейдерам представление о потенциальных разворотах или продолжениях движения цены путем сравнения ценовых движений с направлением индикатора.
Значение дивергенции:
Дивергенция происходит, когда цена актива движется в направлении, противоположном техническому индикатору, что часто сигнализирует об ослаблении тренда или надвигающемся развороте. Это полезно для определения момента, когда тренд может приближаться к коррекции или полному развороту.
Типы дивергенции:
- Бычья дивергенция наблюдается, когда цена актива достигает более низких минимумов, но индикатор, такой как RSI, начинает показывать более высокие минимумы, что говорит об ослаблении нисходящего импульса. Это может означать, что давление со стороны продавцов ослабевает, что потенциально подготавливает почву для восходящего движения цен.
- Медвежья дивергенция наблюдается, когда цена достигает более высоких максимумов, но индикатор показывает более низкие максимумы, что указывает на ослабление восходящего импульса. Это может предвещать снижение цены.
История вопроса:
Дивергенция — ключевая концепция технического анализа, влияющая на поведение рынка и стратегии трейдеров. Барт и Массе (Bart and Masse) в своей работе "Расхождение мнений и риск" (Divergence of Opinion and Risk) 1981 года подчеркивают, как расхождения мнений о движении рынка могут увеличить риск и волатильность цен, отражая роль дивергенции в техническом анализе.
Эмпирические данные, собранные Тиленуи и Шивараджем (Tilehnouei and Shivaraj, 2013), подразумевают, что инструменты, подобные MACD, могут превосходить RSI в определенных контекстах, предлагая ценную информацию о динамике рынка посредством сигналов дивергенции. На основании этого исследования мы можем сказать, что интеграция дивергенции с другими индикаторами, такими как взаимодействие между RSI, MACD и ценовым движением, усиливает ее полезность в комплексном подходе к торговле, что подтверждается различными отраслевыми источниками.
В следующем разделе мы реализуем идею дивергенции в советнике.
Шаги по интеграции обнаружения расхождений
Чтобы включить обнаружение дивергенции в MQL5-советник, начнем с расчета значений индекса относительной силы (RSI) с использованием таких функций, как iRSI(), и сравнивая их с динамикой цен. Значительные ценовые экстремумы определяются с помощью 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 IndexH4- (более низкая минимальная цена B против более высокого минимального значения RSI в точке D)
Медвежья дивергенция Boom 300 IndexH4 (более высокая максимальная цена 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 IndexH4: Бычья скрытая дивергенция
Boom 300 IndexH4: Медвежья скрытая дивергенция
Интеграция этих типов дивергенции в советник требует кодирования условий для их обнаружения на каждом тике или закрытии бара с использованием таких инструментов, как iRSI() или iMACD() для расчета индикаторов. После обнаружения сигналы дивергенции сопоставляются с ограничениями тренда, определяемыми с использованием ежедневных рыночных настроений.
Улучшения советника Trend Constraint: Реализация новых условий исполнения сделок для использования дивергенции
Когда дивергенция наконец обнаружена, нам понадобится дополнительный индикатор для подтверждения и подачи сигнала об исполнении ордера. Есть много вариантов, но мы рассмотрим возможность использования MACD и RSI. Вот список других дополнительных индикаторов подтверждения:
- Полосы Боллинджера
- Стохастический осциллятор
- Балансовый объем (OBV) и взвешенная по объему средняя цена (VWAP)
- Индекс среднего направления движения (ADX)
Схождение-расхождение скользящих средних (MACD):
Зачем использовать: MACD может подтверждать изменения импульса. Если обнаружено расхождение с RSI, MACD может предоставить вторичное подтверждение силы или ослабления тренда.
Как это работает:
Советник будет отслеживать пересечения линий MACD или изменения гистограммы, соответствующие результатам дивергенции. Например, медвежья дивергенция может быть подтверждена, если линия MACD пересекает сигнальную линию сверху вниз или гистограмма начинает снижаться.
Основные характеристики индикатора MACD:
Ниже показано, как просмотреть встроенный индикатор, такой как MACD, в папке Examples внутри папки Indicators в MetaEditor 5.
MetaEditor 5: Доступ к исходному файлу 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
Инициализируем хэндл MACD и выделим буферную память во время OnInit().
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: Торговая логика
Во-первых, стратегия обеспечивает возможность торговли на основе дивергенции. Можно открыть не более 3 открытых позиций на основе дивергенции. Извлекаются данные буфера MACD, попытка повторяется в случае неудачи, сделки совершаются только на полных барах. Кроме того, сделки согласуются с дневными трендами свечей, гарантируя, что покупки будут совершаться только в бычьи дни, а продажи — только в медвежьи.
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 для подсчета всех открытых позиций с указанным магическим числом. Это обеспечивает соблюдение максимального лимита позиции, установленного для сделок на основе дивергенции.
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; }
Магическое число:
Другим важным аспектом является придание нашим позициям идентичности. В этом случае мы присвоили уникальный магический номер сделкам, регулируемым стратегией дивергенции.
// Ensure the magic number is set for the trade trade.SetExpertMagicNumber(DivergenceMagicNumber);
Шаг 7: Защита прибыли
Мы внедрили функцию защиты прибыли, которая включает логику фиксации прибыли для нашего советника Trend Constraint, чтобы динамически защищать прибыль от открытых сделок. Функция 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()
Вызовем логику торговли по дивергенции в основном цикле советника.
void OnTick() { CheckDivergenceTrading(); }
Шаг 9: Завершение
void OnDeinit(const int reason) { IndicatorRelease(macd_handle); }
Интеграция стратегии в основной советник Trend Constrant.
В предыдущей статье этой серии мы разработали советник на основе канала Дончиана, объединив две стратегии в одном советнике. Внедрим третью стратегию и используем логические данные для включения и отключения стратегий.
// 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
Устанавливаем стратегию Divergence на 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 }
Результаты тестирования на истории и практическое применение
Для проведения нашего теста нам необходимо найти советника по ограничению тренда в разделе "Эксперты" терминала. Убедитесь, что вы используете демо-счет. Откроется окно тестера стратегий, в котором можно настроить входные параметры для различных оптимизаций. Изображения ниже получены с настройками по умолчанию:
Советник Trend Constraint: демо-настройки
Мы запустили тестер стратегий, и сделки были успешно совершены. Система ограничила максимальное количество позиций тремя за сессию, что предотвратило множественные неконтролируемые исполнения ордеров. Ниже представлено изображение, демонстрирующее фрагмент тестирования на истории в действии:
Советник Trend Constraint: Тестирование на EURUSD M15
На изображении ниже показано, что наши функции управления позициями работают так, как и ожидалось. Согласно реализованной логике, активны максимум три ордера, каждый из которых помечен комментарием Divergence Sell, что позволяет легко идентифицировать их.
Советник Trend Constraint: максимум 3 позиции за сессию
Заключение
Мы изучили различные типы дивергенций и реализовали их в коде как часть комплексной стратегии, которая объединяет такие индикаторы, как RSI и MACD, для генерации точных торговых сигналов. Эти сигналы были дополнительно уточнены путем включения ограничений тренда дневных свечей, что обеспечило их соответствие более широким трендам и повысило надежность. Наш советник Trend Constraint теперь включает в себя три отдельные настраиваемые стратегии, позволяющие пользователям настраивать советник в соответствии со своими торговыми предпочтениями и адаптироваться к различным рыночным условиям.
Для улучшения управления торговлей мы внедрили такие функции, как уникальные магические числа для каждой позиции, что позволяет точно контролировать открытые сделки и ограничивать количество позиций на стратегию. Кроме того, была разработана специальная функция фиксации прибыли, которая позволяет сохранять прибыль путем динамической корректировки уровней стоп-лосса в случае, если рынок развернется до достижения цели тейк-профита. Этот функционал обеспечивает как управление рисками, так и гибкость, делая советник надежным и адаптируемым. Ниже прилагаются исходные файлы советника. Мы призываем вас изучить их, протестировать различные конфигурации и поделиться своими отзывами в комментариях. Обратите внимание: примеры приведены только в образовательных целях, и все тесты должны проводиться на демо-счетах.
Успехов в разработке!
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16549




- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования