//+------------------------------------------------------------------+ //| TrailingsFunc.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Простой трал по значению | //+------------------------------------------------------------------+ void SimpleTrailingByValue(const double value_sl, const long magic=-1, const int trailing_step_pt=0, const int trailing_start_pt=0, const int trailing_offset_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(request.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; } //+------------------------------------------------------------------+ //| Возвращает размер 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); } //+------------------------------------------------------------------+ //| Возвращает описание таймфрейма | //+------------------------------------------------------------------+ string TimeframeDescription(const ENUM_TIMEFRAMES timeframe) { return(StringSubstr(EnumToString(timeframe==PERIOD_CURRENT ? Period() : timeframe), 7)); } //+------------------------------------------------------------------+ //| Возвращает флаг открытия нового бара таймсерии | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| Возвращает время открытия бара по индексу таймсерии | //+------------------------------------------------------------------+ 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]; } //+------------------------------------------------------------------+ //| Возвращает данные индикатора по хендлу | //| с указанного индекса таймсерии | //+------------------------------------------------------------------+ double GetIndData(const int handle_ind, const int index) { double array[1]; ResetLastError(); if(CopyBuffer(handle_ind, 0, index, 1, array)!=1) { PrintFormat("%s: CopyBuffer() failed. Error %d", __FUNCTION__, GetLastError()); return EMPTY_VALUE; } return array[0]; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Трал по данным индикатора, указанного по хендлу | //+------------------------------------------------------------------+ void TrailingByDataInd(const int handle_ind, const int index=1, const long magic=-1, const int trailing_step_pt=0, const int trailing_start_pt=0, const int trailing_offset_pt=0) { //--- получаем значение Parabolic SAR с указанного индекса таймсерии double data=GetIndData(handle_ind, index); //--- если данные получить не удалось - уходим if(data==EMPTY_VALUE) return; //--- вызываем функцию простого трала с указанием цены для StopLoss, полученной от Parabolic SAR SimpleTrailingByValue(data, magic, trailing_step_pt, trailing_start_pt, trailing_offset_pt); } //+------------------------------------------------------------------+ //| Создаёт и возвращает хэндл Parabolic SAR | //+------------------------------------------------------------------+ int CreateSAR(const string symbol_name, const ENUM_TIMEFRAMES timeframe, const double step_sar=0.02, const double max_sar=0.2) { //--- устанавливаем параметры индикатора в допустимых пределах double step=(step_sar<0.0001 ? 0.0001 : step_sar); double max =(max_sar <0.0001 ? 0.0001 : max_sar); //--- корректируем значения символа и таймфрейма ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe); string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name); //--- создаём хэндл индикатора ResetLastError(); int handle=iSAR(symbol, period, step, max); //--- при ошибке создания индикатора выводим сообщение об ошибке в журнал if(handle==INVALID_HANDLE) { PrintFormat("Failed to create iSAR(%s, %s, %.3f, %.2f) handle. Error %d", symbol, TimeframeDescription(period), step, max, GetLastError()); } //--- возвращаем результат создания хэндла индикатора return handle; } //+------------------------------------------------------------------+ //| Создаёт и возвращает хэндл Adaptive Moving Average | //+------------------------------------------------------------------+ int CreateAMA(const string symbol_name, const ENUM_TIMEFRAMES timeframe, const int ama_period=9, const int fast_ema_period=2, const int slow_ema_period=30, const int shift=0, const ENUM_APPLIED_PRICE price=PRICE_CLOSE) { //--- устанавливаем параметры индикатора в допустимых пределах int ma_period=(ama_period<1 ? 9 : ama_period); int fast_ema=(fast_ema_period<1 ? 2 : fast_ema_period); int slow_ema=(slow_ema_period<1 ? 30 : slow_ema_period); //--- корректируем значения символа и таймфрейма ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe); string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name); //--- создаём хэндл индикатора ::ResetLastError(); int handle=::iAMA(symbol, period, ma_period, fast_ema, slow_ema, shift, price); //--- при ошибке создания индикатора выводим сообщение об ошибке в журнал if(handle==INVALID_HANDLE) { ::PrintFormat("Failed to create iAMA(%s, %s, %d, %d, %d, %s) handle. Error %d", symbol, TimeframeDescription(period), ma_period, fast_ema, slow_ema, ::StringSubstr(::EnumToString(price),6), ::GetLastError()); } //--- возвращаем результат создания хэндла индикатора return handle; } //+------------------------------------------------------------------+ //| Создаёт и возвращает хэндл Double Exponential Moving Average | //+------------------------------------------------------------------+ int CreateDEMA(const string symbol_name, const ENUM_TIMEFRAMES timeframe, const int dema_period=14, const int shift=0, const ENUM_APPLIED_PRICE price=PRICE_CLOSE) { //--- устанавливаем параметры индикатора в допустимых пределах int ma_period=(dema_period<1 ? 14 : dema_period); //--- корректируем значения символа и таймфрейма ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe); string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name); //--- создаём хэндл индикатора ::ResetLastError(); int handle=::iDEMA(symbol, period, ma_period, shift, price); //--- при ошибке создания индикатора выводим сообщение об ошибке в журнал if(handle==INVALID_HANDLE) { ::PrintFormat("Failed to create iDEMA(%s, %s, %d, %s) handle. Error %d", symbol, TimeframeDescription(period), ma_period, ::StringSubstr(::EnumToString(price),6), ::GetLastError()); } //--- возвращаем результат создания хэндла индикатора return handle; } //+------------------------------------------------------------------+ //| Создаёт и возвращает хэндл Fractal Adaptive Moving Average | //+------------------------------------------------------------------+ int CreateFRAMA(const string symbol_name, const ENUM_TIMEFRAMES timeframe, const int frama_period=14, const int shift=0, const ENUM_APPLIED_PRICE price=PRICE_CLOSE) { //--- устанавливаем параметры индикатора в допустимых пределах int ma_period=(frama_period<1 ? 14 : frama_period); //--- корректируем значения символа и таймфрейма ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe); string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name); //--- создаём хэндл индикатора ::ResetLastError(); int handle=::iFrAMA(symbol, period, ma_period, shift, price); //--- при ошибке создания индикатора выводим сообщение об ошибке в журнал if(handle==INVALID_HANDLE) { ::PrintFormat("Failed to create iFrAMA(%s, %s, %d, %s) handle. Error %d", symbol, TimeframeDescription(period), ma_period, ::StringSubstr(::EnumToString(price),6), ::GetLastError()); } //--- возвращаем результат создания хэндла индикатора return handle; } //+------------------------------------------------------------------+ //| Создаёт и возвращает хэндл Moving Average | //+------------------------------------------------------------------+ int CreateMA(const string symbol_name, const ENUM_TIMEFRAMES timeframe, const int period_ma=10, const int shift=0, const ENUM_MA_METHOD method=MODE_SMA, const ENUM_APPLIED_PRICE price=PRICE_CLOSE) { //--- устанавливаем параметры индикатора в допустимых пределах int ma_period=(period_ma<1 ? 14 : period_ma); //--- корректируем значения символа и таймфрейма ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe); string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name); //--- создаём хэндл индикатора ::ResetLastError(); int handle=::iMA(symbol, period, ma_period, shift, method, price); //--- при ошибке создания индикатора выводим сообщение об ошибке в журнал if(handle==INVALID_HANDLE) { ::PrintFormat("Failed to create iMA(%s, %s, %d, %s, %s) handle. Error %d", symbol, TimeframeDescription(period), ma_period, ::StringSubstr(::EnumToString(method),5), ::StringSubstr(::EnumToString(price),6), ::GetLastError()); } //--- возвращаем результат создания хэндла индикатора return handle; } //+------------------------------------------------------------------+ //| Создаёт и возвращает хэндл Triple Exponential Moving Average | //+------------------------------------------------------------------+ int CreateTEMA(const string symbol_name, const ENUM_TIMEFRAMES timeframe, const int tema_period=14, const int shift=0, const ENUM_APPLIED_PRICE price=PRICE_CLOSE) { //--- устанавливаем параметры индикатора в допустимых пределах int ma_period=(tema_period<1 ? 14 : tema_period); //--- корректируем значения символа и таймфрейма ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe); string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name); //--- создаём хэндл индикатора ::ResetLastError(); int handle=::iTEMA(symbol, period, ma_period, shift, price); //--- при ошибке создания индикатора выводим сообщение об ошибке в журнал if(handle==INVALID_HANDLE) { ::PrintFormat("Failed to create iTEMA(%s, %s, %d, %s) handle. Error %d", symbol, TimeframeDescription(period), ma_period, ::StringSubstr(::EnumToString(price),6), ::GetLastError()); } //--- возвращаем результат создания хэндла индикатора return handle; } //+------------------------------------------------------------------+ //| Создаёт и возвращает хэндл Variable Index Dynamyc Average | //+------------------------------------------------------------------+ int CreateVIDYA(const string symbol_name, const ENUM_TIMEFRAMES timeframe, const int period_cmo=9, const int period_ema=12, const int shift=0, const ENUM_APPLIED_PRICE price=PRICE_CLOSE) { //--- устанавливаем параметры индикатора в допустимых пределах int ma_period =(period_cmo<1 ? 9 : period_cmo); int ema_period=(period_ema<1 ? 12 : period_ema); //--- корректируем значения символа и таймфрейма ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe); string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name); //--- создаём хэндл индикатора ::ResetLastError(); int handle=::iVIDyA(symbol, period, ma_period, ema_period, shift, price); //--- при ошибке создания индикатора выводим сообщение об ошибке в журнал if(handle==INVALID_HANDLE) { ::PrintFormat("Failed to create iVIDyA(%s, %s, %d, %d, %s) handle. Error %d", symbol, TimeframeDescription(period), ma_period, ema_period, ::StringSubstr(::EnumToString(price),6), ::GetLastError()); } //--- возвращаем результат создания хэндла индикатора return handle; } //+------------------------------------------------------------------+