
Построение модели для ограничения диапазона сигналов по тренду (Часть 8): Разработка советника (I)
Содержание:
- Введение
- Решение задач по отображению прямоугольников риска и прибыли
- Создание советника на основе индикатора
- Тестирование
- Заключение
Введение
MetaEditor включает в себя компилятор, который эффективно обрабатывает ошибки, обнаруженные во время попыток профилирования. Этот инструмент помог мне понять, почему предыдущая версия не отображала прямоугольники риска и прибыли так, как надо. Хотя программа успешно компилировалась, проблема была не в самом коде. В диапазоне исторических свечей ничего не отображалось, в первую очередь из-за технических особенностей. - Значение исторической свечи (look back candle) по умолчанию было установлено слишком высоким — 5000 баров.
- Наличие нескольких буферов в одной программе увеличивает сложность вычислений, что может замедлить отображение окна графика индикатора.
Теперь перейдем к основной цели статьи - разработке советника на основе усовершенствованного индикатора Trend Constraint. Ниже представлен скриншот, показывающий, как отдельный скрипт успешно решил проблему, которую мы изначально намеревались решить с помощью основного индикатора.
Автоматически отрисованные прямоугольники риска и вознаграждения
Решение задач по отображению прямоугольников риска и прибыли
Для решения проблем индикатора:- Мы сократили период ретроспективного анализа (look-back period) с 5000 до 1000 баров, тем самым значительно уменьшив объем данных для расчета.
- Мы стремились снизить нагрузку на программу, создав автономный скрипт как часть набора инструментов. Этот скрипт специально проверяет условия, обрабатываемые буферами 6 и 7 в индикаторе. После выполнения этих условий скрипт рисует необходимые прямоугольники риска-прибыли и размещает линии с ценовыми метками для цены входа, стоп-лосса и тейк-профита. Однако важно отметить, что скрипт выполняет одноразовую задачу и не работает непрерывно. Пользователь должен вручную добавить скрипт на график, чтобы визуализировать торговые уровни, представленные нарисованными объектами и ценовыми отметками.
Ниже представлено изображение, демонстрирующее запуск скрипта:
Скрипт Trend Constraint R-R: Построение прямоугольников риска и прибыли при пересечении скользящей средней
Изолировав эту функцию, мы гарантируем бесперебойную работу нашего индикатора, избегая зависаний компьютера или торгового терминала. Использование прямоугольников риска и прибыли, а также указание уровней выхода позволяет трейдерам визуально оценивать направление и цели торговли заранее. Таким образом можно вести ручную торговлю без необходимости использования советника. Скрипт с необходимой логикой отработал идеально. Ниже приведен полный код скрипта.//+------------------------------------------------------------------+ //| Trend Constraint R-R.mq5 | //| Script program | //+------------------------------------------------------------------+ #property strict #property script_show_inputs #property copyright "2024 Clemence Benjamin" #property version "1.00" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property description "A script program for drawing risk and rewars rectangles based on Moving Averaage crossover." //--- input parameters input int FastMAPeriod = 14; input int SlowMAPeriod = 50; input double RiskHeightPoints = 5000.0; // Default height of the risk rectangle in points input double RewardHeightPoints = 15000.0; // Default height of the reward rectangle in points input color RiskColor = clrIndianRed; // Default risk color input color RewardColor = clrSpringGreen; // Default reward color input int MaxBars = 500; // Maximum bars to process input int RectangleWidth = 10; // Width of the rectangle in bars input bool FillRectangles = true; // Option to fill rectangles input int FillTransparency = 128; // Transparency level (0-255), 128 is 50% transparency //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- delete existing rectangles and lines DeleteExistingObjects(); //--- declare and initialize variables int i, limit; double FastMA[], SlowMA[]; double closePrice, riskLevel, rewardLevel; //--- calculate moving averages if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0) { Print("Error in calculating moving averages."); return; } ArraySetAsSeries(FastMA, true); ArraySetAsSeries(SlowMA, true); CopyBuffer(iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, FastMA); CopyBuffer(iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, SlowMA); limit = MathMin(ArraySize(FastMA), ArraySize(SlowMA)); for (i = 1; i < limit - 1; i++) { //--- check for crossover if (FastMA[i] > SlowMA[i] && FastMA[i - 1] <= SlowMA[i - 1]) { //--- long position entry point (bullish crossover) closePrice = iClose(NULL, 0, i); riskLevel = closePrice + RiskHeightPoints * Point(); rewardLevel = closePrice - RewardHeightPoints * Point(); //--- draw risk rectangle DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor); //--- draw reward rectangle DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor); //--- draw entry, stop loss, and take profit lines DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits)); DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits)); DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits)); } else if (FastMA[i] < SlowMA[i] && FastMA[i - 1] >= SlowMA[i - 1]) { //--- short position entry point (bearish crossover) closePrice = iClose(NULL, 0, i); riskLevel = closePrice - RiskHeightPoints * Point(); rewardLevel = closePrice + RewardHeightPoints * Point(); //--- draw risk rectangle DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor); //--- draw reward rectangle DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor); //--- draw entry, stop loss, and take profit lines DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits)); DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits)); DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits)); } } } //+------------------------------------------------------------------+ //| Function to delete existing rectangles and lines | //+------------------------------------------------------------------+ void DeleteExistingObjects() { int totalObjects = ObjectsTotal(0, 0, -1); for (int i = totalObjects - 1; i >= 0; i--) { string name = ObjectName(0, i, 0, -1); if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 || StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 || StringFind(name, "TakeProfit_") >= 0) { ObjectDelete(0, name); } } } //+------------------------------------------------------------------+ //| Function to draw rectangles | //+------------------------------------------------------------------+ void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor) { if (ObjectFind(0, name) >= 0) ObjectDelete(0, name); datetime startTime = iTime(NULL, 0, startBar); datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar); if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice)) Print("Failed to create rectangle: ", name); // Set the color with transparency (alpha value) int alphaValue = FillTransparency; // Adjust transparency level (0-255) color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24); // Combine alpha with RGB ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background if (FillRectangles) { ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor); // Fill color with transparency } else { ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF); // No fill color } } //+------------------------------------------------------------------+ //| Function to draw price lines | //+------------------------------------------------------------------+ void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText) { datetime time = iTime(NULL, 0, barIndex); datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth); // Extend line to the right if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price)) Print("Failed to create price line: ", name); ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background // Create text label string labelName = name + "_Label"; if (ObjectFind(0, labelName) >= 0) ObjectDelete(0, labelName); if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price)) Print("Failed to create label: ", labelName); ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_LEFT); ObjectSetString(0, labelName, OBJPROP_TEXT, labelText); ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); ObjectSetInteger(0, labelName, OBJPROP_XOFFSET, 5); ObjectSetInteger(0, labelName, OBJPROP_YOFFSET, 0); }
Давайте подробно обсудим работу скрипта, прежде чем приступить к разработке советника:
- Мы обеспечили возможность настройки ключевых аспектов торговой стратегии путем определения входных параметров. Мы предоставили возможность настраивать периоды быстрой и медленной скользящих средних, задавать размеры и цвета прямоугольников риска и прибыли, определять максимальное количество баров для обработки и выбирать, следует ли заполнять прямоугольники цветом. Такая настройка позволяет адаптировать скрипт к различным торговым стратегиям в соответствии с предпочтениями пользователя, что и сформировало раздел входных параметров нашей программы:
//---- Input Parameters input int FastMAPeriod = 14; input int SlowMAPeriod = 50; input double RiskHeightPoints = 5000.0; input double RewardHeightPoints = 15000.0; input color RiskColor = clrIndianRed; input color RewardColor = clrSpringGreen; input int MaxBars = 500; input int RectangleWidth = 10; input bool FillRectangles = true; input int FillTransparency = 128;
- В функции (OnStart) мы позаботились о том, чтобы график оставался чистым, запрограммировав скрипт на предварительное удаление любых существующих прямоугольников риска/прибыли и ценовых линий. Затем мы рассчитали быстрые и медленные скользящие средние с помощью функции (iMA) и сохранили эти значения в массивах для дальнейшей обработки. По мере того, как скрипт проходит по барам на графике, мы установили условия для обнаружения бычьих пересечений, когда быстрая скользящая средняя пересекает медленную снизу вверх. При выполнении этих условий скрипт рассчитывает цену входа, уровень риска (стоп-лосс) и уровень прибыли (тейк-профит). Затем он рисует прямоугольники и ценовые линии на графике, эффективно отмечая эти критические торговые уровни, как мы увидим далее во фрагментах внутреннего кода:
//----Onstart Function void OnStart() { //--- delete existing rectangles and lines DeleteExistingObjects(); //--- declare and initialize variables int i, limit; double FastMA[], SlowMA[]; double closePrice, riskLevel, rewardLevel; //--- calculate moving averages if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0) { Print("Error in calculating moving averages."); return; }
- Для сохранения ясности на графике мы разработали функцию Delete Existing Objects (удалить существующие объекты), которую скрипт использует для удаления любых ранее нарисованных объектов, связанных с торговыми сигналами. Проверяя названия всех объектов на графике, скрипт гарантировал, что будет отображаться только самая последняя и актуальная информация, защищая график от захламления:
//---- DeleteAllExistingObjects Function void DeleteExistingObjects() { int totalObjects = ObjectsTotal(0, 0, -1); for (int i = totalObjects - 1; i >= 0; i--) { string name = ObjectName(0, i, 0, -1); if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 || StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 || StringFind(name, "TakeProfit_") >= 0) { ObjectDelete(0, name); } } }
- В функции Draw Rectangle (нарисовать прямоугольник) мы гарантировали, что скрипт сможет визуально отображать уровни риска и прибыли, предварительно удалив все существующие прямоугольники с одинаковыми именами, чтобы избежать дублирования. Затем мы заставили скрипт вычислить время начала и окончания для прямоугольников на основе индексов баров, а также установили цвета и уровни прозрачности. Это позволило выделить прямоугольники на графике, не заслоняя другие важные детали:
///---Draw rectangle function void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor) { if (ObjectFind(0, name) >= 0) ObjectDelete(0, name); datetime startTime = iTime(NULL, 0, startBar); datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar); if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice)) Print("Failed to create rectangle: ", name); int alphaValue = FillTransparency; color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24); ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); if (FillRectangles) { ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor); } else { ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF); } }
- Наконец, мы реализовали функцию Draw Price Line (нарисовать линию цены), чтобы дать скрипту команду добавлять горизонтальные линии на уровнях входа, стоп-лосса и тейк-профита. Скрипт продлил эти линии по всему графику и добавил текстовые метки, отображающие соответствующие уровни цен. Это обеспечило визуальный справочник, позволяющий пользователям быстро определять и управлять ключевыми точками для сделок на основе сигналов, генерируемых скользящими средними:
///---- Draw Price Lines Function void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText) { datetime time = iTime(NULL, 0, barIndex); datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth); if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price)) Print("Failed to create price line: ", name); ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); string labelName = name + "_Label"; if (ObjectFind(0, labelName) >= 0) ObjectDelete(0, labelName); if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price)) Print("Failed to create label: ", labelName); ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10); ObjectSetString(0, labelName, OBJPROP_TEXT, labelText); ObjectSetInteger(0, labelName, OBJPROP_BACK, true); }
Теперь, скрипт можно запускать регулярно как часть торгового инструментария, чтобы видеть прошлые и текущие торговые уровни. Теперь займемся советником. Я опишу весь процесс разработки окончательной версии рабочего советника. В этой статье мы сосредоточимся на том, как заставить его работать вместе с ранее созданным индикатором Trend Constraint V1.09.
Создание советника на основе индикатора
Чтобы создать советника на MQL5 с использованием пользовательского индикатора, нам необходимо убедиться, что файл пользовательского индикатора (.ex5) доступен в папке Indicators платформы MetaTrader 5, в данном случае это Trend Constraint V1.09. Используя MetaEditor, мы можем написать наш советник, включив функции MQL5 для доступа к значениям буфера индикатора. Мы используем функцию (iCustom()) для вызова пользовательского индикатора в советнике, указав необходимые параметры, такие как символ и таймфрейм.
Для извлечения данных из буферов индикатора мы используем функцию (CopyBuffer()), которая извлекает значения буфера, которые мы собираемся анализировать на предмет торговых сигналов. Затем мы реализуем нашу торговую логику на основе этих значений буфера, определяя условия открытия, закрытия или изменения ордеров в соответствии с нашей стратегией. Интегрируем функции управления рисками, такие как стоп-лосс и тейк-профит, для разумного управления торговлей. Проведем тестирование советника на истории с использованием тестера стратегий MetaTrader 5 для оценки его производительности и точной настройки его параметров. Наконец, мы проверим функциональность советника на демо-счете, чтобы убедиться в его эффективной работе в реальных рыночных условиях, перед выходом на реальный рынок.
Мы можем начать с запуска шаблона советника в MetaEditor, а затем разработать или изменить его на основе примера, показанного скриншоте ниже:
Запуск шаблона советника в MetaEditor
Разделим разработку на шесть этапов. По мере продвижения я рекомендую вводить фрагменты кода непосредственно в MetaEditor. Этот практический подход поможет вам лучше понять и усвоить шаги, особенно если вы новичок в разработке советников.
1. Заголовок и метаданные
В заголовке мы определяем название и назначение нашего советника. Включая информацию об авторских правах, ссылку на наш профиль и указывая версию, мы гарантируем, что наш советник можно будет легко идентифицировать и отслеживать. Эти метаданные помогают нам и другим пользователям понять происхождение и цель советника, особенно когда код редактируется или используется в совместной разработке:
//You can replace the author details with yours. //+------------------------------------------------------------------+ //| Trend Constraint Expert.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property strict #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.0" #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09"
2. Входные параметры
Здесь мы определяем ключевые входные параметры, которые позволяют нам настраивать поведение советника, не изменяя сам код. Устанавливая такие параметры, как лоты, проскальзывание, стоп-лосс, тейк-профит и магическое число, мы делаем советник гибким и адаптируемым к различным торговым стратегиям. Магическое число особенно важно, поскольку оно позволяет нам однозначно идентифицировать сделки, совершенные этим советником, что имеет решающее значение при использовании нескольких советников или совершении ручных сделок:
///----------- EA inputs parameters for customizations input double Lots = 0.1; // Lot size input int Slippage = 3; // Slippage input double StopLoss = 50; // Stop Loss in points input double TakeProfit = 100; // Take Profit in points input int MagicNumber = 123456; // Magic number for orders
3. Функция инициализации (OnInit)
В функции OnInit мы подготавливаем почву для работы советника, инициализируя необходимые компоненты. Начнем с попытки получить хэндл для нашего индикатора Trend Constraint V1.09. Он позволит нам взаимодействовать с индикатором программно. Если хэндл успешно получен, приступаем к последовательной настройке массивов буферов (Buffer6 и Buffer7), что позволяет нам хранить и обрабатывать значения индикаторов. Если же хэндл не может быть получен, советник вернет ошибку инициализации с описанием, которое поможет нам найти проблему:
////-------Initialization Function int OnInit() { //--- Get the indicator handle indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09"); if (indicator_handle < 0) { Print("Failed to get the indicator handle. Error: ", GetLastError()); return(INIT_FAILED); } //--- Set the buffer arrays as series ArraySetAsSeries(Buffer6, true); ArraySetAsSeries(Buffer7, true); return(INIT_SUCCEEDED); }
4. Функция деинициализации (OnDeinit)
Когда наш советник удаляется с графика или когда платформа закрывается, выполняется функция (OnDeinit). Здесь мы позаботимся об освобождении дескриптора индикатора, гарантируя освобождение всех ресурсов, которые были выделены во время работы советника. Этот этап имеет решающее значение для поддержания эффективности и стабильности нашей торговой среды, поскольку он предотвращает ненужное потребление ресурсов:
///------Deinitialization Function(OnDeinit) void OnDeinit(const int reason) { //--- Release the indicator handle IndicatorRelease(indicator_handle); }
5. Основная функция исполнения (OnTick)
Функция (OnTick) — это место, где происходят реальные торговые действия. Эта функция вызывается каждый раз, когда поступает новый рыночный тик. Сначала мы проверяем, нет ли уже открытой позиции с таким же магическим числом, чтобы не дублировать сделки. Далее мы копируем последние значения из буферов индикаторов (Buffer6 и Buffer7) для принятия торговых решений. Если наши условия для сигнала на покупку или продажу выполнены, мы формируем и отправляем соответствующий торговый запрос. Необходимо указать все необходимые параметры, такие как тип ордера, цена, стоп-лосс, тейк-профит и проскальзывание, для эффективной реализации нашей торговой стратегии:
///---Main Execution Function(OnTick) void OnTick() { //--- Check if there is already an open position with the same MagicNumber if (PositionSelect(Symbol())) { if (PositionGetInteger(POSITION_MAGIC) == MagicNumber) { return; // Exit OnTick if there's an open position with the same MagicNumber } } //--- Calculate the indicator if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0) { Print("Failed to copy buffer values. Error: ", GetLastError()); return; } //--- Check for a buy signal if (Buffer7[0] != EMPTY_VALUE) { double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits); double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits); //--- Prepare the buy order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_BUY; request.price = ask; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Buy Order"; //--- Send the buy order if (!OrderSend(request, result)) { Print("Error opening buy order: ", result.retcode); } else { Print("Buy order opened successfully! Ticket: ", result.order); } } //--- Check for a sell signal if (Buffer6[0] != EMPTY_VALUE) { double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits); double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits); //--- Prepare the sell order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_SELL; request.price = bid; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Sell Order"; //--- Send the sell order if (!OrderSend(request, result)) { Print("Error opening sell order: ", result.retcode); } else { Print("Sell order opened successfully! Ticket: ", result.order); } } }
6. Прочие функции
Мы также включаем несколько других функций для обработки различных событий, с которыми может столкнуться наш советник. Они входят в шаблон советника и в настоящее время не используются для простоты:
- (OnTrade) - здесь мы можем обрабатывать любые конкретные действия, которые необходимо выполнить при возникновении торгового события. Хотя в настоящее время эта функция пуста, она предоставляет нам пространство для добавления логики при необходимости.
- (OnTester) - функция используется для пользовательских расчетов во время тестирования на истории. Возвращая значение, мы можем оптимизировать нашу стратегию на основе определенных показателей.
- (OnTesterInit, OnTesterPass, OnTesterDeinit) - эти функции участвуют в оптимизации в тестере стратегий. Они позволяют нам инициализировать настройки, выполнять действия после каждого прохода оптимизации и впоследствии очищать ресурсы.
- (OnChartEvent) - эта функция позволяет нам обрабатывать различные события графика, такие как щелчки мыши или нажатия клавиш. Хотя в настоящее время она пустует, мы можем использовать это пространство для добавления интерактивных функций в наш советник:
///----Other Template functions available void OnTrade() { //--- Handle trade events if necessary } double OnTester() { double ret = 0.0; //--- Custom calculations for strategy tester return (ret); } void OnTesterInit() { //--- Initialization for the strategy tester } void OnTesterPass() { //--- Code executed after each pass in optimization } void OnTesterDeinit() { //--- Cleanup after tester runs } void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Handle chart events here }
В окончательном виде программа будет выглядеть так:
//+------------------------------------------------------------------+ //| Trend Constraint Expert.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property strict #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.0" #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09" //--- Input parameters for the EA input double Lots = 0.1; // Lot size input int Slippage = 3; // Slippage input double StopLoss = 50; // Stop Loss in points input double TakeProfit = 100; // Take Profit in points input int MagicNumber = 123456; // Magic number for orders //--- Indicator handle int indicator_handle; double Buffer6[]; double Buffer7[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Get the indicator handle indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09"); if (indicator_handle < 0) { Print("Failed to get the indicator handle. Error: ", GetLastError()); return(INIT_FAILED); } //--- Set the buffer arrays as series ArraySetAsSeries(Buffer6, true); ArraySetAsSeries(Buffer7, true); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Release the indicator handle IndicatorRelease(indicator_handle); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Check if there is already an open position with the same MagicNumber if (PositionSelect(Symbol())) { if (PositionGetInteger(POSITION_MAGIC) == MagicNumber) { return; // Exit OnTick if there's an open position with the same MagicNumber } } //--- Calculate the indicator if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0) { Print("Failed to copy buffer values. Error: ", GetLastError()); return; } //--- Check for a buy signal if (Buffer7[0] != EMPTY_VALUE) { double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits); double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits); //--- Prepare the buy order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_BUY; request.price = ask; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Buy Order"; //--- Send the buy order if (!OrderSend(request, result)) { Print("Error opening buy order: ", result.retcode); } else { Print("Buy order opened successfully! Ticket: ", result.order); } } //--- Check for a sell signal if (Buffer6[0] != EMPTY_VALUE) { double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits); double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits); //--- Prepare the sell order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_SELL; request.price = bid; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Sell Order"; //--- Send the sell order if (!OrderSend(request, result)) { Print("Error opening sell order: ", result.retcode); } else { Print("Sell order opened successfully! Ticket: ", result.order); } } } //+------------------------------------------------------------------+ //| Trade function | //+------------------------------------------------------------------+ void OnTrade() { //--- Handle trade events if necessary } //+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { double ret = 0.0; //--- Custom calculations for strategy tester return (ret); } //+------------------------------------------------------------------+ //| TesterInit function | //+------------------------------------------------------------------+ void OnTesterInit() { //--- Initialization for the strategy tester } //+------------------------------------------------------------------+ //| TesterPass function | //+------------------------------------------------------------------+ void OnTesterPass() { //--- Code executed after each pass in optimization } //+------------------------------------------------------------------+ //| TesterDeinit function | //+------------------------------------------------------------------+ void OnTesterDeinit() { //--- Cleanup after tester runs } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Handle chart events here } //+------------------------------------------------------------------+
После успешной компиляции программу пора протестировать.
Тест
В MetaEditor мы можем использовать кнопку "Компилировать", чтобы подготовить нашу программу к тестированию. В данном случае компиляция прошла успешно, и мы запустили тест на индексе Boom 500 с помощью тестера стратегий.
Запуск тестера из навигатора
Откроется панель тестера стратегий, позволяющая вам настроить некоторые параметры. Как только всё будет готово, нажмите "Старт" в правом нижнем углу. Например, размер лота по умолчанию в советнике установлен на уровне 0,1 лота, но для индекса Boom 500 мне пришлось увеличить его до минимума в 0,2 лота.
Панель тестера стратегий
Удивительно, но наша система хорошо показала себя в тестере стратегий, как видно из скриншота ниже:
Визуализация тестера стратегий: советник Trend Constraint
Заключение
Добавление прямоугольников риска и прибыли дает трейдерам четкое графическое представление их сделок, что упрощает мониторинг и управление открытыми позициями. Это наглядное пособие особенно полезно на быстро меняющихся рынках, где необходимо быстро принимать решения. Прямоугольники служат постоянным напоминанием о потенциальных результатах сделки, помогая трейдерам придерживаться своего первоначального торгового плана.
Успешное взаимодействие индикатора (Trend Constraint V1.09) и советника подчеркивает важность синергии между инструментами в торговой стратегии. Индикатор определяет потенциальные тренды и развороты, а советник совершает сделки и управляет рисками на основе этой информации. Такой комплексный подход приводит к более целостной и эффективной торговой стратегии.
Ниже прилагаются используемые индикатор, скрипт и советник. Возможности для улучшения и модификации сохраняются. Надеюсь, статья оказалась для вас ценной. Вы можете поделиться своими мыслями в комментариях. Удачной торговли!
Приложенные файлы | Описание |
---|---|
Trend_Constraint V1.09.mq5 | Исходный код индикатора для работы с советником. |
Trend Constraint R-R.mq5 | Исходный код скрипта для отображения прямоугольников риска и прибыли. |
Trend Constraint Expert.mq5 | Исходный код советника, работающего строго с Trend Constraint V1.09 |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/15321





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