iSAR с его встроенным динамическим шагом, обеспечивая защиту прибыли и минимизацию рисков.Исходный код
Представляем класс CTrailingSAR, который инкапсулирует всю логику работы с индикатором Parabolic SAR и управления Stop Loss для открытых позиций. Этот подход позволяет легко интегрировать трейлинг-стоп в любой эксперт MQL5, обеспечивая чистоту кода и модульность.
Класс инициализирует индикатор iSAR с заданными параметрами и предоставляет метод CheckAndModifyPositions(), который необходимо вызывать в основном цикле советника (например, в OnTick()). Метод перебирает все открытые позиции, фильтрует их по символу и магическому номеру, а затем рассчитывает новый уровень Stop Loss на основе значения Parabolic SAR с учетом небольшого буфера.
//+------------------------------------------------------------------+
//| CTrailingSAR.mqh |
//| FinFluct.com |
//+------------------------------------------------------------------+
#property copyright "FinFluct.com"
#property link "https://finfluct.com"
#property version "1.00"
#property strict
class CTrailingSAR
{
private:
int _sarHandle; // Handle для индикатора Parabolic SAR
double _sarStep; // Шаг (acceleration factor) для SAR
double _sarMaximum; // Максимум (maximum acceleration factor) для SAR
int _magicNumber; // Магический номер для фильтрации позиций
int _slippage; // Допустимое проскальзывание в пунктах
double _bufferPips; // Буфер в пунктах от SAR для Stop Loss
ENUM_TIMEFRAMES _timeframe; // Таймфрейм для расчета SAR
//--- Вспомогательные функции
double GetSARValue(int shift);
double NormalizePrice(double price);
double PipsToPoints(double pips);
public:
//--- Конструктор
CTrailingSAR(double sarStep, double sarMaximum, int magicNumber, int slippage, double bufferPips, ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT);
//--- Деструктор
~CTrailingSAR();
//--- Основная функция для проверки и модификации позиций
void CheckAndModifyPositions();
};
//+------------------------------------------------------------------+
//| Конструктор |
//+------------------------------------------------------------------+
CTrailingSAR::CTrailingSAR(double sarStep, double sarMaximum, int magicNumber, int slippage, double bufferPips, ENUM_TIMEFRAMES timeframe)
{
_sarStep = sarStep;
_sarMaximum = sarMaximum;
_magicNumber = magicNumber;
_slippage = slippage;
_bufferPips = bufferPips;
_timeframe = timeframe;
// Инициализация индикатора Parabolic SAR
_sarHandle = iSAR(Symbol(), _timeframe, _sarStep, _sarMaximum);
if (_sarHandle == INVALID_HANDLE)
{
Print("Ошибка при создании индикатора iSAR. Код ошибки: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Деструктор |
//+------------------------------------------------------------------+
CTrailingSAR::~CTrailingSAR()
{
if (_sarHandle != INVALID_HANDLE)
{
IndicatorRelease(_sarHandle);
}
}
//+------------------------------------------------------------------+
//| Получение значения SAR для заданного сдвига |
//+------------------------------------------------------------------+
double CTrailingSAR::GetSARValue(int shift)
{
if (_sarHandle == INVALID_HANDLE)
{
return 0.0;
}
double sar_values[1];
if (CopyBuffer(_sarHandle, 0, shift, 1, sar_values) <= 0)
{
Print("Ошибка при копировании буфера SAR. Код ошибки: ", GetLastError());
return 0.0;
}
return sar_values[0];
}
//+------------------------------------------------------------------+
//| Нормализация цены в соответствии с символом |
//+------------------------------------------------------------------+
double CTrailingSAR::NormalizePrice(double price)
{
return NormalizeDouble(price, _Digits);
}
//+------------------------------------------------------------------+
//| Перевод пунктов в пункты (для удобства, если Point() отличается)|
//+------------------------------------------------------------------+
double CTrailingSAR::PipsToPoints(double pips)
{
// Определяем фактический размер пункта для текущего символа.
// Для 5-значных символов (например, EURUSD), 1 пипс = 10 * _Point (0.0001 против 0.00001).
// Для 4-значных символов (например, EURUSD), 1 пипс = 1 * _Point (0.0001 против 0.0001).
// Для 3-значных JPY символов (например, USDJPY), 1 пипс = 10 * _Point (0.01 против 0.001).
// Для 2-значных JPY символов (например, USDJPY), 1 пипс = 1 * _Point (0.01 против 0.01).
// Общая проверка: если количество знаков после запятой (Digits) равно 5 или 3, то это "дробные пипсы".
if (SymbolInfoInteger(Symbol(), SYMBOL_DIGITS) == 5 || SymbolInfoInteger(Symbol(), SYMBOL_DIGITS) == 3)
{
return pips * _Point * 10;
}
return pips * _Point;
}
//+------------------------------------------------------------------+
//| Основная функция для проверки и модификации позиций |
//+------------------------------------------------------------------+
void CTrailingSAR::CheckAndModifyPositions()
{
if (_sarHandle == INVALID_HANDLE)
{
return;
}
// Получаем текущие значения SAR и цены
// Используем SAR с предыдущего закрытого бара (shift=1) для стабильности
double currentSAR = GetSARValue(1);
double currentPriceBid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
double currentPriceAsk = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
if (currentSAR == 0.0) // Если SAR не удалось получить (например, недостаточно баров)
{
return;
}
// Перебираем все открытые позиции
for (int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong positionTicket = PositionGetTicket(i);
if (positionTicket == 0)
{
continue;
}
if (!PositionSelectByTicket(positionTicket))
{
Print("Ошибка при выборе позиции ", positionTicket, ". Код ошибки: ", GetLastError());
continue;
}
// Фильтруем по символу и магическому номеру
if (PositionGetString(POSITION_SYMBOL) != Symbol() || PositionGetInteger(POSITION_MAGIC) != _magicNumber)
{
continue;
}
ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double currentSL = PositionGetDouble(POSITION_SL);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double newSL = 0.0;
bool modifyNeeded = false;
// Определяем новый Stop Loss в зависимости от типа позиции
if (positionType == POSITION_TYPE_BUY)
{
// Для BUY позиций SAR должен быть ниже текущей цены, указывая на восходящий тренд
if (currentSAR < currentPriceBid)
{
// Новый SL = SAR минус буфер (чтобы дать цене немного пространства)
newSL = NormalizePrice(currentSAR - PipsToPoints(_bufferPips));
// Условие для модификации: новый SL должен быть в прибыли ИЛИ текущий SL отсутствует
// И новый SL должен быть выше текущего SL (только подтягиваем, не опускаем)
if (newSL > openPrice) // Убеждаемся, что новый SL находится в прибыльной зоне
{
if (currentSL == 0.0 || newSL > currentSL)
{
modifyNeeded = true;
}
}
}
}
else if (positionType == POSITION_TYPE_SELL)
{
// Для SELL позиций SAR должен быть выше текущей цены, указывая на нисходящий тренд
if (currentSAR > currentPriceAsk)
{
// Новый SL = SAR плюс буфер
newSL = NormalizePrice(currentSAR + PipsToPoints(_bufferPips));
// Условие для модификации: новый SL должен быть в прибыли ИЛИ текущий SL отсутствует
// И новый SL должен быть ниже текущего SL (только подтягиваем, не поднимаем)
if (newSL < openPrice) // Убеждаемся, что новый SL находится в прибыльной зоне
{
if (currentSL == 0.0 || newSL < currentSL)
{
modifyNeeded = true;
}
}
}
}
// Модифицируем позицию, если необходимо
if (modifyNeeded)
{
MqlTradeRequest request;
MqlTradeResult result;
ZeroMemory(request);
ZeroMemory(result);
request.action = TRADE_ACTION_SLTP;
request.position = positionTicket;
request.sl = newSL;
request.tp = PositionGetDouble(POSITION_TP); // Сохраняем текущий TP
request.deviation = _slippage;
request.symbol = PositionGetString(POSITION_SYMBOL);
if (!OrderSend(request, result))
{
Print("Ошибка при модификации позиции ", positionTicket, ": ", GetLastError(), ". Результат: ", result.retcode);
// Возможно, стоит отправить уведомление, например, через Pushover.
// См. статью: MT5 и Pushover: Критические PUSH-уведомления через WebRequest
}
else
{
if (result.retcode == TRADE_RETCODE_DONE)
{
Print("Позиция ", positionTicket, " SL модифицирован на ", newSL);
}
else
{
Print("Не удалось модифицировать позицию ", positionTicket, ". Код ошибки: ", result.retcode);
}
}
}
}
}
Разбор параметров
Класс CTrailingSAR принимает следующие параметры в своем конструкторе, которые определяют поведение индикатора и логику трейлинг-стопа:
sarStep: Начальный шаг (acceleration factor) для индикатора Parabolic SAR. Определяет, насколько быстро SAR приближается к цене. Типичные значения: 0.02.sarMaximum: Максимальный шаг (maximum acceleration factor) для индикатора Parabolic SAR. Ограничивает скорость приближения SAR к цене. Типичные значения: 0.2.magicNumber: Уникальный магический номер, используемый для идентификации позиций, открытых вашим советником. Это позволяет трейлинг-стопу управлять только «своими» позициями.slippage: Допустимое проскальзывание в пунктах при модификации Stop Loss. Защищает от слишком больших отклонений цены при исполнении ордера.bufferPips: Буфер в пипсах, который добавляется или вычитается от значения SAR для установки Stop Loss. Это создает небольшое расстояние между SAR и фактическим SL, чтобы избежать преждевременных срабатываний из-за рыночного шума.timeframe: Таймфрейм, на котором будет рассчитываться индикатор Parabolic SAR. По умолчанию используется текущий таймфрейм графика (PERIOD_CURRENT).
Как запустить
Для использования функции трейлинг-стопа в вашем эксперте MQL5 выполните следующие шаги:
-
Создайте файл класса: Сохраните приведенный выше код класса
CTrailingSARв отдельном файле, например,CTrailingSAR.mqh, в папкеMQL5/Includeвашего терминала MT5. -
Подключите класс к эксперту: В вашем файле эксперта (
.mq5) добавьте директиву#includeв начале файла:#include -
Объявите внешние параметры: Добавьте внешние переменные для настройки параметров SAR и трейлинг-стопа. Это позволит вам легко изменять их через окно настроек советника:
input double InpSARStep = 0.02; // Шаг SAR input double InpSARMaximum = 0.2; // Максимум SAR input int InpMagicNumber = 12345; // Магический номер input int InpSlippage = 5; // Проскальзывание в пунктах input double InpBufferPips = 5.0; // Буфер от SAR в пипсах input ENUM_TIMEFRAMES InpSARTimeframe = PERIOD_CURRENT; // Таймфрейм SAR CTrailingSAR *g_trailingSAR = NULL; // Глобальный указатель на объект класса -
Инициализация в
OnInit(): Создайте экземпляр классаCTrailingSARв функцииOnInit():int OnInit() { g_trailingSAR = new CTrailingSAR(InpSARStep, InpSARMaximum, InpMagicNumber, InpSlippage, InpBufferPips, InpSARTimeframe); if (g_trailingSAR == NULL) { Print("Ошибка инициализации CTrailingSAR."); return INIT_FAILED; } return INIT_SUCCEEDED; } -
Деинициализация в
OnDeinit(): Освободите память, удалив объект класса в функцииOnDeinit():void OnDeinit(const int reason) { if (g_trailingSAR != NULL) { delete g_trailingSAR; g_trailingSAR = NULL; } } -
Вызов в
OnTick(): Вызывайте методCheckAndModifyPositions()в функцииOnTick()для постоянного мониторинга и корректировки Stop Loss:void OnTick() { if (g_trailingSAR != NULL) { g_trailingSAR->CheckAndModifyPositions(); } }
Таким образом, ваш эксперт будет автоматически управлять Stop Loss, следуя за индикатором Parabolic SAR. Для более продвинутого управления советником, например, с интерактивными кнопками, вы можете изучить статью MQL5: Панель управления советником с кнопками Buy/Sell и вводом лота. Если вы планируете создавать более сложные системы, требующие взаимодействия между несколькими терминалами, вам может быть полезна статья Связь между терминалами MT5 через Named Pipes на MQL5.




