MQL5: Массовое изменение Stop Loss и Take Profit по Magic Number

MQL5: Массовое изменение Stop Loss и Take Profit по Magic Number MQL4 / MQL5 (MetaTrader)
Подробная инструкция и MQL5 скрипт для массового изменения уровней Stop Loss и Take Profit у открытых ордеров по заданному Magic Number в MQL5. Управляйте своими позициями эффективно.
Суть: Скрипт MQL5 для массового изменения уровней Stop Loss и Take Profit у всех открытых ордеров на текущем символе, принадлежащих одному Magic Number. Позволяет задать новые уровни SL/TP в пунктах относительно цены открытия ордера, с учетом минимального отступа брокера.

Введение

В активном трейдинге, особенно при использовании нескольких экспертных советников или ручной торговле с большим количеством позиций, возникает необходимость оперативно управлять параметрами ордеров. Ручное изменение уровней Stop Loss (SL) и Take Profit (TP) для десятков или сотен ордеров может быть крайне трудоемким и неэффективным. Именно для таких сценариев мы, как Senior Quant Developers, разработали специализированный MQL5 скрипт.

Этот скрипт позволяет массово изменить SL и TP для всех открытых ордеров, которые имеют определенный Magic Number, на текущем торговом символе. Это особенно полезно, когда вы хотите быстро адаптировать стратегию управления рисками или прибылью для группы ордеров, запущенных одним экспертом или по одной торговой идее. Например, если вы используете несколько стратегий, каждая из которых имеет свой Magic Number, вы можете легко управлять ими по отдельности. Если вас интересуют другие аспекты автоматизации, рекомендуем ознакомиться с нашим скриптом для сбора статистики проскальзываний Market ордеров.

Исходный код

Представленный ниже MQL5 скрипт предназначен для выполнения массовых операций по изменению SL и TP. Он тщательно проверяет все открытые позиции, фильтрует их по заданному Magic Number и символу, а затем корректирует уровни SL/TP, учитывая при этом минимальные требования брокера к отступам (TradeStopLevel). Скрипт также включает функцию подтверждения, чтобы избежать случайных изменений.


//+------------------------------------------------------------------+
//|                                     MassModifySLTPbyMagicNumber.mq5|
//|                                        Copyright 2023, FinFluct|
//|                                            https://finfluct.com|
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, FinFluct"
#property link      "https://finfluct.com"
#property version   "1.00"
#property description "Скрипт для массового изменения уровней Stop Loss и Take Profit"
#property description "у всех открытых ордеров по одному Magic Number."
#property script_show_inputs

//--- Входные параметры
input long   MagicNumberToModify = 12345; // Magic Number ордеров для изменения
input int    NewStopLossPips     = 50;    // Новый Stop Loss в пунктах (0 - не изменять)
input int    NewTakeProfitPips   = 100;   // Новый Take Profit в пунктах (0 - не изменять)
input bool   ConfirmModification = true;  // Подтверждение перед изменением

//--- Глобальные переменные для работы с символом
MQL_SYMBOL_INFO _SymbolInfo;

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
    // Получаем информацию о текущем символе
    if (!SymbolInfoGet(_Symbol, _SymbolInfo))
    {
        Print("Ошибка получения информации о символе ", _Symbol, ": ", GetLastError());
        return;
    }

    // Проверка на нулевые значения SL/TP, если оба 0, то нет смысла запускать
    if (NewStopLossPips == 0 && NewTakeProfitPips == 0)
    {
        Print("Оба значения Stop Loss и Take Profit равны 0. Изменения не требуются.");
        return;
    }

    // Подтверждение действия
    if (ConfirmModification)
    {
        string message = "Вы уверены, что хотите изменить SL/TP для всех ордеров с Magic Number " +
                         (string)MagicNumberToModify + " на символе " + _Symbol + "?\n\n";
        if (NewStopLossPips > 0) message += "Новый SL: " + (string)NewStopLossPips + " пунктов\n";
        else message += "SL останется без изменений\n";
        if (NewTakeProfitPips > 0) message += "Новый TP: " + (string)NewTakeProfitPips + " пунктов";
        else message += "TP останется без изменений";

        if (MessageBox(message, "Подтверждение массового изменения SL/TP", MB_YESNO | MB_ICONQUESTION) == IDNO)
        {
            Print("Действие отменено пользователем.");
            return;
        }
    }

    int modifiedCount = 0;
    int totalPositions = PositionsTotal();

    if (totalPositions == 0)
    {
        Print("Открытых позиций не найдено.");
        return;
    }

    Print("Начинаем проверку ", totalPositions, " открытых позиций на символе ", _Symbol, "...");

    // Итерируем по всем открытым позициям
    for (int i = totalPositions - 1; i >= 0; i--)
    {
        string symbol = PositionGetSymbol(i);
        if (symbol == _Symbol) // Работаем только с позициями по текущему символу
        {
            long magic = PositionGetInteger(POSITION_MAGIC);

            if (magic == MagicNumberToModify)
            {
                ulong ticket = PositionGetTicket(i);
                ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
                double currentSL = PositionGetDouble(POSITION_SL);
                double currentTP = PositionGetDouble(POSITION_TP);
                double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);

                double newSL = currentSL; // Инициализируем текущими значениями
                double newTP = currentTP; // Инициализируем текущими значениями
                
                double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
                double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

                // Расчет нового Stop Loss
                if (NewStopLossPips > 0)
                {
                    // 1 стандартный пункт (pip) = 10 * _SymbolInfo.Point для большинства инструментов (например, EURUSD 5-знак)
                    double slPriceOffset = NewStopLossPips * _SymbolInfo.Point * 10; 
                    if (type == POSITION_TYPE_BUY)
                    {
                        newSL = NormalizeDouble(openPrice - slPriceOffset, _SymbolInfo.Digits);
                    }
                    else if (type == POSITION_TYPE_SELL)
                    {
                        newSL = NormalizeDouble(openPrice + slPriceOffset, _SymbolInfo.Digits);
                    }
                }

                // Расчет нового Take Profit
                if (NewTakeProfitPips > 0)
                {
                    double tpPriceOffset = NewTakeProfitPips * _SymbolInfo.Point * 10;
                    if (type == POSITION_TYPE_BUY)
                    {
                        newTP = NormalizeDouble(openPrice + tpPriceOffset, _SymbolInfo.Digits);
                    }
                    else if (type == POSITION_TYPE_SELL)
                    {
                        newTP = NormalizeDouble(openPrice - tpPriceOffset, _SymbolInfo.Digits);
                    }
                }

                // Проверка на минимальный отступ от текущей цены (TradeStopLevel)
                // TradeStopLevel возвращается в пунктах (points), поэтому умножаем на _SymbolInfo.Point
                double minDistance = _SymbolInfo.TradeStopLevel * _SymbolInfo.Point;

                // Корректировка SL, если он слишком близко к текущей цене
                if (newSL != 0) // Если SL установлен
                {
                    if (type == POSITION_TYPE_BUY)
                    {
                        if (newSL > currentAsk - minDistance)
                        {
                            newSL = NormalizeDouble(currentAsk - minDistance, _SymbolInfo.Digits);
                            Print("Внимание: SL для BUY ордера #", ticket, " скорректирован до ", newSL, " из-за TradeStopLevel.");
                        }
                    }
                    else if (type == POSITION_TYPE_SELL)
                    {
                        if (newSL < currentBid + minDistance)
                        {
                            newSL = NormalizeDouble(currentBid + minDistance, _SymbolInfo.Digits);
                            Print("Внимание: SL для SELL ордера #", ticket, " скорректирован до ", newSL, " из-за TradeStopLevel.");
                        }
                    }
                }

                // Корректировка TP, если он слишком близко к текущей цене
                if (newTP != 0) // Если TP установлен
                {
                    if (type == POSITION_TYPE_BUY)
                    {
                        if (newTP < currentAsk + minDistance)
                        {
                            newTP = NormalizeDouble(currentAsk + minDistance, _SymbolInfo.Digits);
                            Print("Внимание: TP для BUY ордера #", ticket, " скорректирован до ", newTP, " из-за TradeStopLevel.");
                        }
                    }
                    else if (type == POSITION_TYPE_SELL)
                    {
                        if (newTP > currentBid - minDistance)
                        {
                            newTP = NormalizeDouble(currentBid - minDistance, _SymbolInfo.Digits);
                            Print("Внимание: TP для SELL ордера #", ticket, " скорректирован до ", newTP, " из-за TradeStopLevel.");
                        }
                    }
                }
                
                // Проверка, нужно ли вообще что-то менять
                // Сравниваем с точностью до Digits, чтобы избежать проблем с плавающей точкой
                if (NormalizeDouble(newSL, _SymbolInfo.Digits) == NormalizeDouble(currentSL, _SymbolInfo.Digits) &&
                    NormalizeDouble(newTP, _SymbolInfo.Digits) == NormalizeDouble(currentTP, _SymbolInfo.Digits))
                {
                    Print("Ордер #", ticket, " (Magic: ", magic, ") - SL/TP уже соответствуют заданным значениям. Пропускаем.");
                    continue;
                }

                // Отправка запроса на изменение
                MqlTradeRequest request;
                MqlTradeResult result;
                ZeroMemory(request);
                ZeroMemory(result);

                request.action = TRADE_ACTION_SLTP;
                request.position = ticket;
                request.sl = newSL;
                request.tp = newTP;
                request.deviation = 0; // Отклонение не требуется для SL/TP

                if (!OrderSend(request, result))
                {
                    Print("Ошибка отправки запроса на изменение ордера #", ticket, ": ", GetLastError());
                }
                else
                {
                    if (result.retcode == TRADE_RETCODE_DONE)
                    {
                        Print("Успешно изменен ордер #", ticket, " (Magic: ", magic, "). Новый SL: ", newSL, ", Новый TP: ", newTP);
                        modifiedCount++;
                    }
                    else
                    {
                        Print("Не удалось изменить ордер #", ticket, " (Magic: ", magic, "). Код ошибки: ", result.retcode, ". Описание: ", result.comment);
                    }
                }
            }
        }
    }

    if (modifiedCount > 0)
    {
        Print("Массовое изменение SL/TP завершено. Изменено ордеров: ", modifiedCount);
    }
    else
    {
        Print("Не найдено ордеров с Magic Number ", MagicNumberToModify, " на символе ", _Symbol, " для изменения SL/TP.");
    }
}

Разбор параметров

Скрипт имеет несколько входных параметров, которые позволяют гибко настроить его поведение:

  • MagicNumberToModify:

    Уникальный идентификатор (Magic Number) ордеров, для которых будут изменяться уровни SL/TP. Скрипт будет обрабатывать только те открытые позиции на текущем символе, чей Magic Number совпадает с этим значением.

  • NewStopLossPips:

    Новое значение Stop Loss в пунктах. Это расстояние будет отсчитываться от цены открытия ордера. Если установить значение 0, Stop Loss для ордеров с указанным Magic Number изменяться не будет (сохранится текущее значение).

  • NewTakeProfitPips:

    Новое значение Take Profit в пунктах. Это расстояние также будет отсчитываться от цены открытия ордера. Если установить значение 0, Take Profit для ордеров с указанным Magic Number изменяться не будет (сохранится текущее значение).

  • ConfirmModification:

    Булевый параметр, который включает или отключает запрос подтверждения перед выполнением массового изменения. Рекомендуется оставлять true для предотвращения случайных действий, особенно на реальном счете.

Как запустить

Для использования скрипта выполните следующие шаги:

  1. Откройте MetaEditor: В MetaTrader 5 нажмите F4 или выберите Сервис -> Редактор MetaQuotes Language.

  2. Создайте новый скрипт: В MetaEditor выберите Файл -> Создать -> Скрипт. Назовите его, например, MassModifySLTPbyMagicNumber.mq5.

  3. Вставьте код: Скопируйте предоставленный выше исходный код и вставьте его в окно нового скрипта, заменив стандартный шаблон.

  4. Скомпилируйте скрипт: Нажмите F7 или кнопку Компилировать на панели инструментов. Убедитесь, что в окне Ошибки нет сообщений об ошибках.

  5. Перейдите в MetaTrader 5: Откройте терминал MetaTrader 5.

  6. Найдите скрипт: В окне Навигатор (обычно слева) разверните раздел Скрипты и найдите MassModifySLTPbyMagicNumber.

  7. Прикрепите к графику: Перетащите скрипт на график того торгового символа, для которого вы хотите изменить ордера (например, EURUSD). Важно: скрипт будет работать только с ордерами на том символе, к графику которого он прикреплен.

  8. Настройте параметры: Появится окно с входными параметрами. Укажите желаемый Magic NumberToModify, а также NewStopLossPips и NewTakeProfitPips. Если вы хотите, чтобы SL или TP не изменялись, оставьте соответствующее значение 0. Убедитесь, что ConfirmModification установлен в true для безопасности.

  9. Разрешите автоматическую торговлю: На вкладке Общие установите флажок Разрешить автоматическую торговлю.

  10. Запустите скрипт: Нажмите ОК. Если ConfirmModification включен, появится диалоговое окно с запросом подтверждения. После подтверждения скрипт выполнит изменения.

  11. Проверьте результат: Откройте вкладку Эксперты в окне Терминал (Ctrl+T), чтобы увидеть логи работы скрипта и подтверждение изменений. Также проверьте свои открытые позиции.

Заключение

Этот скрипт является мощным инструментом для эффективного управления рисками и прибылью в вашей торговой стратегии. Он позволяет быстро реагировать на изменения рынка, корректируя параметры множества ордеров одновременно, что значительно экономит время и снижает вероятность ошибок по сравнению с ручным методом. Подобные инструменты автоматизации, как и скрипт для массового закрытия убыточных позиций, являются неотъемлемой частью арсенала современного трейдера.

Для тех, кто стремится к более глубокой оптимизации торговых стратегий, мы также рекомендуем изучить методы автоматического управления объемами ордеров, например, с помощью автоматического выравнивания объемов ордеров по Келли (Lot Sizing), что позволит вам еще точнее контролировать риски и максимизировать потенциальную прибыль.

Оцените статью
FinFluct