Article-14782-MQL5-Trailing.../TrailingBySAR_01.mq5
2026-03-22 17:00:39 +07:00

255 行
23 KiB
MQL5

//+------------------------------------------------------------------+
//| TrailingBySAR_01.mq5 |
//| Copyright 2024, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#define SAR_DATA_INDEX 1 // бар, с которого получаем данные Parabolic SAR
//--- input parameters
input ENUM_TIMEFRAMES InpTimeframeSAR = PERIOD_CURRENT; // Parabolic SAR Timeframe
input double InpStepSAR = 0.02; // Parabolic SAR Step
input double InpMaximumSAR = 0.2; // Parabolic SAR Maximum
//--- global variables
int ExtHandleSAR =INVALID_HANDLE; // хэндл Parabolic SAR
double ExtStepSAR =0; // шаг Parabolic SAR
double ExtMaximumSAR=0; // максимум Parabolic SAR
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- устанавливаем параметры Parabolic SAR в допустимых пределах
ExtStepSAR =(InpStepSAR<0.0001 ? 0.0001 : InpStepSAR);
ExtMaximumSAR=(InpMaximumSAR<0.0001 ? 0.0001 : InpMaximumSAR);
//--- при ошибке создания индикатора выводим сообщение в журнал и выходим с ошибкой из OnInit
ExtHandleSAR =iSAR(Symbol(), InpTimeframeSAR, ExtStepSAR, ExtMaximumSAR);
if(ExtHandleSAR==INVALID_HANDLE)
{
PrintFormat("Failed to create iSAR(%s, %s, %.3f, %.2f) handle. Error %d",
Symbol(), TimeframeDescription(InpTimeframeSAR), ExtStepSAR, ExtMaximumSAR, GetLastError());
return(INIT_FAILED);
}
//--- успешно
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- если не новый бар - уходим из обработчика
if(!IsNewBar())
return;
//--- тралим стопы позиций по Parabolic SAR
TrailingStopBySAR();
}
//+------------------------------------------------------------------+
//| TradeTransaction function |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
const MqlTradeRequest& request,
const MqlTradeResult& result)
{
if(trans.type==TRADE_TRANSACTION_DEAL_ADD)
TrailingStopBySAR();
}
//+------------------------------------------------------------------+
//| Возвращает описание таймфрейма |
//+------------------------------------------------------------------+
string TimeframeDescription(const ENUM_TIMEFRAMES timeframe)
{
return(StringSubstr(EnumToString(timeframe==PERIOD_CURRENT ? Period() : timeframe), 7));
}
//+------------------------------------------------------------------+
//| Возвращает время открытия бара по индексу таймсерии |
//+------------------------------------------------------------------+
datetime TimeOpenBar(const int index)
{
datetime array[1];
ResetLastError();
if(CopyTime(NULL, PERIOD_CURRENT, index, 1, array)!=1)
{
PrintFormat("%s: CopyTime() failed. Error %d", __FUNCTION__, GetLastError());
return 0;
}
return array[0];
}
//+------------------------------------------------------------------+
//| Возвращает флаг открытия нового бара таймсерии |
//+------------------------------------------------------------------+
bool IsNewBar(void)
{
static datetime time_prev=0;
datetime bar_open_time=TimeOpenBar(0);
if(bar_open_time==0)
return false;
if(bar_open_time!=time_prev)
{
time_prev=bar_open_time;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Возвращает данные Parabolic SAR с указанного индекса таймсерии |
//+------------------------------------------------------------------+
double GetSARData(const int index)
{
double array[1];
ResetLastError();
if(CopyBuffer(ExtHandleSAR, 0, index, 1, array)!=1)
{
PrintFormat("%s: CopyBuffer() failed. Error %d", __FUNCTION__, GetLastError());
return EMPTY_VALUE;
}
return array[0];
}
//+------------------------------------------------------------------+
//| Возвращает размер StopLevel текущего символа в пунктах |
//+------------------------------------------------------------------+
int StopLevel(const int spread_multiplier)
{
int spread =(int)SymbolInfoInteger(Symbol(), SYMBOL_SPREAD);
int stop_level=(int)SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL);
return(stop_level==0 ? spread * spread_multiplier : stop_level);
}
//+------------------------------------------------------------------+
//| Функция трейлинга стопа по значению цены StopLoss |
//+------------------------------------------------------------------+
void TrailingStopByValue(const double value_sl, const long magic=-1, const int trailing_step_pt=0, const int trailing_start_pt=0)
{
//--- структура цен
MqlTick tick={};
//--- в цикле по общему количеству открытых позиций
int total=PositionsTotal();
for(int i=total-1; i>=0; i--)
{
//--- получаем тикет очередной позиции
ulong pos_ticket=PositionGetTicket(i);
if(pos_ticket==0)
continue;
//--- получаем символ и магик позиции
string pos_symbol = PositionGetString(POSITION_SYMBOL);
long pos_magic = PositionGetInteger(POSITION_MAGIC);
//--- пропускаем позиции, не соответствующие фильтру по символу и магику
if((magic!=-1 && pos_magic!=magic) || pos_symbol!=Symbol())
continue;
//--- если цены получить не удалось - идём далее
if(!SymbolInfoTick(Symbol(), tick))
continue;
//--- получаем тип позиции, цену её открытия и уровень StopLoss
ENUM_POSITION_TYPE pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double pos_open=PositionGetDouble(POSITION_PRICE_OPEN);
double pos_sl =PositionGetDouble(POSITION_SL);
//--- если условия для модификации StopLoss подходят - модифицируем стоп позиции
if(CheckCriterion(pos_type, pos_open, pos_sl, value_sl, trailing_step_pt, trailing_start_pt, tick))
ModifySL(pos_ticket, value_sl);
}
}
//+------------------------------------------------------------------+
//|Проверяет критерии модификации StopLoss позициии и возвращает флаг|
//+------------------------------------------------------------------+
bool CheckCriterion(ENUM_POSITION_TYPE pos_type, double pos_open, double pos_sl, double value_sl,
int trailing_step_pt, int trailing_start_pt, MqlTick &tick)
{
//--- если стоп позиции и уровень стопа для модификации равны - возвращаем false
if(NormalizeDouble(pos_sl-value_sl, Digits())==0)
return false;
double trailing_step = trailing_step_pt * Point(); // переводим шаг трала в цену
double stop_level = StopLevel(2) * Point(); // переводим уровень StopLevel символа в цену
int pos_profit_pt = 0; // прибыль позиции в пунктах
//--- в зависимости от типа позиции проверяем условия для модицикации StopLoss
switch(pos_type)
{
//--- длинная позиция
case POSITION_TYPE_BUY :
pos_profit_pt=int((tick.bid - pos_open) / Point()); // рассчитываем прибыль позиции в пунктах
if(tick.bid - stop_level > value_sl // если цена и отложенный от неё уровень StopLevel выше уровня StopLoss (соблюдена дистанция по StopLevel)
&& pos_sl + trailing_step < value_sl // если уровень StopLoss выше, чем шаг трала, отложенный от текущего StopLoss позиции
&& (trailing_start_pt==0 || pos_profit_pt>trailing_start_pt) // если тралим при любой прибыли или прибыль позиции в пунктах больше значения начала трейлинга - возвращаем true
)
return true;
break;
//--- короткая позиция
case POSITION_TYPE_SELL :
pos_profit_pt=int((pos_open - tick.ask) / Point()); // рассчитываем прибыль позиции в пунктах
if(tick.ask + stop_level < value_sl // если цена и отложенный от неё уровень StopLevel ниже уровня StopLoss (соблюдена дистанция по StopLevel)
&& (pos_sl - trailing_step > value_sl || pos_sl==0) // если уровень StopLoss ниже, чем шаг трала, отложенный от текущего StopLoss позиции или у позиции ещё не установлен StopLoss
&& (trailing_start_pt==0 || pos_profit_pt>trailing_start_pt) // если тралим при любой прибыли или прибыль позиции в пунктах больше значения начала трейлинга - возвращаем true
)
return true;
break;
//--- по умолчанию вернём false
default: break;
}
//--- нет подходящих критериев
return false;
}
//+------------------------------------------------------------------+
//| Модифицирует StopLoss позиции по тикету |
//+------------------------------------------------------------------+
bool ModifySL(const ulong ticket, const double stop_loss)
{
//--- если позицию не удалось выбрать по тикету - сообщаем об этом в журнал и возвращаем false
ResetLastError();
if(!PositionSelectByTicket(ticket))
{
PrintFormat("%s: Failed to select position by ticket number %I64u. Error %d", __FUNCTION__, ticket, GetLastError());
return false;
}
//--- объявляем структуры торгового запроса и результата запроса
MqlTradeRequest request={};
MqlTradeResult result ={};
//--- заполняем структуру запроса
request.action = TRADE_ACTION_SLTP;
request.symbol = PositionGetString(POSITION_SYMBOL);
request.magic = PositionGetInteger(POSITION_MAGIC);
request.tp = PositionGetDouble(POSITION_TP);
request.position = ticket;
request.sl = NormalizeDouble(stop_loss,(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS));
//--- если торговую операцию отправить не удалось - сообщаем об этом в журнал и возвращаем false
if(!OrderSend(request, result))
{
PrintFormat("%s: OrderSend() failed to modify position #%I64u. Error %d",__FUNCTION__, ticket, GetLastError());
return false;
}
//--- успешно отправлен запрос на изменение StopLoss позиции
return true;
}
//+------------------------------------------------------------------+
//| Функция трейлинга StopLoss по индикатору Parabolic SAR |
//+------------------------------------------------------------------+
void TrailingStopBySAR(const long magic=-1, const int trailing_step_pt=0, const int trailing_start_pt=0)
{
//--- получаем значение Parabolic SAR с первого бара таймсерии
double sar=GetSARData(SAR_DATA_INDEX);
//--- если данные получить не удалось - уходим
if(sar==EMPTY_VALUE)
return;
//--- вызываем функцию трала с указанием цены StopLoss, полученной от Parabolic SAR
TrailingStopByValue(sar, magic, trailing_step_pt, trailing_start_pt);
}
//+------------------------------------------------------------------+