Article-14782-MQL5-Trailing.../TrailingsFunc.mqh

397 lines
38 KiB
MQL5
Raw Permalink Normal View History

2026-03-22 16:52:40 +07:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| TrailingsFunc.mqh |
//| Copyright 2024, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
2026-03-22 17:01:15 +07:00
#property copyright "Copyright 2024, MetaQuotes Ltd."
2026-03-22 16:52:40 +07:00
#property link "https://www.mql5.com"
//+------------------------------------------------------------------+
//| @>AB>9 B@0; ?> 7=0G5=8N |
//+------------------------------------------------------------------+
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)
{
//--- AB@C:BC@0 F5=
MqlTick tick={};
//--- 2 F8:;5 ?> >1I5<C :>;8G5AB2C >B:@KBKE ?>78F89
int total=PositionsTotal();
for(int i=total-1; i>=0; i--)
{
//--- ?>;CG05< B8:5B >G5@54=>9 ?>78F88
ulong pos_ticket=PositionGetTicket(i);
if(pos_ticket==0)
continue;
//--- ?>;CG05< A8<2>; 8 <038: ?>78F88
string pos_symbol = PositionGetString(POSITION_SYMBOL);
long pos_magic = PositionGetInteger(POSITION_MAGIC);
//--- ?@>?CA:05< ?>78F88, =5 A>>B25BAB2CNI85 D8;LB@C ?> A8<2>;C 8 <038:C
if((magic!=-1 && pos_magic!=magic) || pos_symbol!=Symbol())
continue;
//--- 5A;8 F5=K ?>;CG8BL =5 C40;>AL - 84Q< 40;55
if(!SymbolInfoTick(Symbol(), tick))
continue;
//--- ?>;CG05< B8? ?>78F88, F5=C 5Q >B:@KB8O 8 C@>25=L 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);
//--- 5A;8 CA;>28O 4;O <>48D8:0F88 StopLoss ?>4E>4OB - <>48D8F8@C5< AB>? ?>78F88
if(CheckCriterion(pos_type, pos_open, pos_sl, value_sl, trailing_step_pt, trailing_start_pt, tick))
ModifySL(pos_ticket, value_sl);
}
}
//+------------------------------------------------------------------+
//|@>25@O5B :@8B5@88 <>48D8:0F88 StopLoss ?>78F888 8 2>72@0I05B D;03|
//+------------------------------------------------------------------+
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)
{
//--- 5A;8 AB>? ?>78F88 8 C@>25=L AB>?0 4;O <>48D8:0F88 @02=K - 2>72@0I05< false
if(NormalizeDouble(pos_sl-value_sl, Digits())==0)
return false;
double trailing_step = trailing_step_pt * Point(); // ?5@52>48< H03 B@0;0 2 F5=C
double stop_level = StopLevel(2) * Point(); // ?5@52>48< C@>25=L StopLevel A8<2>;0 2 F5=C
int pos_profit_pt = 0; // ?@81K;L ?>78F88 2 ?C=:B0E
//--- 2 7028A8<>AB8 >B B8?0 ?>78F88 ?@>25@O5< CA;>28O 4;O <>48F8:0F88 StopLoss
switch(pos_type)
{
//--- 4;8==0O ?>78F8O
case POSITION_TYPE_BUY :
pos_profit_pt=int((tick.bid - pos_open) / Point()); // @0AAG8BK205< ?@81K;L ?>78F88 2 ?C=:B0E
if(tick.bid - stop_level > value_sl // 5A;8 F5=0 8 >B;>65==K9 >B =5Q C@>25=L StopLevel 2KH5 C@>2=O StopLoss (A>1;N45=0 48AB0=F8O ?> StopLevel)
&& pos_sl + trailing_step < value_sl // 5A;8 C@>25=L StopLoss 2KH5, G5< H03 B@0;0, >B;>65==K9 >B B5:CI53> StopLoss ?>78F88
&& (trailing_start_pt==0 || pos_profit_pt>trailing_start_pt) // 5A;8 B@0;8< ?@8 ;N1>9 ?@81K;8 8;8 ?@81K;L ?>78F88 2 ?C=:B0E 1>;LH5 7=0G5=8O =0G0;0 B@59;8=30 - 2>72@0I05< true
)
return true;
break;
//--- :>@>B:0O ?>78F8O
case POSITION_TYPE_SELL :
pos_profit_pt=int((pos_open - tick.ask) / Point()); // @0AAG8BK205< ?@81K;L ?>78F88 2 ?C=:B0E
if(tick.ask + stop_level < value_sl // 5A;8 F5=0 8 >B;>65==K9 >B =5Q C@>25=L StopLevel =865 C@>2=O StopLoss (A>1;N45=0 48AB0=F8O ?> StopLevel)
&& (pos_sl - trailing_step > value_sl || pos_sl==0) // 5A;8 C@>25=L StopLoss =865, G5< H03 B@0;0, >B;>65==K9 >B B5:CI53> StopLoss ?>78F88 8;8 C ?>78F88 5IQ =5 CAB0=>2;5= StopLoss
&& (trailing_start_pt==0 || pos_profit_pt>trailing_start_pt) // 5A;8 B@0;8< ?@8 ;N1>9 ?@81K;8 8;8 ?@81K;L ?>78F88 2 ?C=:B0E 1>;LH5 7=0G5=8O =0G0;0 B@59;8=30 - 2>72@0I05< true
)
return true;
break;
//--- ?> C<>;G0=8N 25@=Q< false
default: break;
}
//--- =5B A>>B25BAB2CNI8E :@8B5@852
return false;
}
//+------------------------------------------------------------------+
//| >48D8F8@C5B StopLoss ?>78F88 ?> B8:5BC |
//+------------------------------------------------------------------+
bool ModifySL(const ulong ticket, const double stop_loss)
{
//--- 5A;8 ?>78F8N =5 C40;>AL 2K1@0BL ?> B8:5BC - A>>1I05< >1 MB>< 2 6C@=0; 8 2>72@0I05< false
ResetLastError();
if(!PositionSelectByTicket(ticket))
{
PrintFormat("%s: Failed to select position by ticket number %I64u. Error %d", __FUNCTION__, ticket, GetLastError());
return false;
}
//--- >1JO2;O5< AB@C:BC@K B>@3>2>3> 70?@>A0 8 @57C;LB0B0 70?@>A0
MqlTradeRequest request={};
MqlTradeResult result ={};
//--- 70?>;=O5< AB@C:BC@C 70?@>A0
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));
//--- 5A;8 B>@3>2CN >?5@0F8N >B?@028BL =5 C40;>AL - A>>1I05< >1 MB>< 2 6C@=0; 8 2>72@0I05< false
if(!OrderSend(request, result))
{
PrintFormat("%s: OrderSend() failed to modify position #%I64u. Error %d",__FUNCTION__, ticket, GetLastError());
return false;
}
//--- CA?5H=> >B?@02;5= 70?@>A =0 87<5=5=85 StopLoss ?>78F88
return true;
}
//+------------------------------------------------------------------+
//| >72@0I05B @07<5@ StopLevel 2 ?C=:B0E |
//+------------------------------------------------------------------+
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);
}
//+------------------------------------------------------------------+
//| >72@0I05B >?8A0=85 B09<D@59<0 |
//+------------------------------------------------------------------+
string TimeframeDescription(const ENUM_TIMEFRAMES timeframe)
{
return(StringSubstr(EnumToString(timeframe==PERIOD_CURRENT ? Period() : timeframe), 7));
}
//+------------------------------------------------------------------+
//| >72@0I05B D;03 >B:@KB8O =>2>3> 10@0 B09<A5@88 |
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
//| >72@0I05B 2@5<O >B:@KB8O 10@0 ?> 8=45:AC B09<A5@88 |
//+------------------------------------------------------------------+
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];
}
//+------------------------------------------------------------------+
//| >72@0I05B 40==K5 8=48:0B>@0 ?> E5=4;C |
//| A C:070==>3> 8=45:A0 B09<A5@88 |
//+------------------------------------------------------------------+
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];
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| "@0; ?> 40==K< 8=48:0B>@0, C:070==>3> ?> E5=4;C |
//+------------------------------------------------------------------+
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)
{
//--- ?>;CG05< 7=0G5=85 Parabolic SAR A C:070==>3> 8=45:A0 B09<A5@88
double data=GetIndData(handle_ind, index);
//--- 5A;8 40==K5 ?>;CG8BL =5 C40;>AL - CE>48<
if(data==EMPTY_VALUE)
return;
//--- 2K7K205< DC=:F8N ?@>AB>3> B@0;0 A C:070=85< F5=K 4;O StopLoss, ?>;CG5==>9 >B Parabolic SAR
SimpleTrailingByValue(data, magic, trailing_step_pt, trailing_start_pt, trailing_offset_pt);
}
//+------------------------------------------------------------------+
//| !>740QB 8 2>72@0I05B EM=4; Parabolic SAR |
//+------------------------------------------------------------------+
int CreateSAR(const string symbol_name, const ENUM_TIMEFRAMES timeframe, const double step_sar=0.02, const double max_sar=0.2)
{
//--- CAB0=02;8205< ?0@0<5B@K 8=48:0B>@0 2 4>?CAB8<KE ?@545;0E
double step=(step_sar<0.0001 ? 0.0001 : step_sar);
double max =(max_sar <0.0001 ? 0.0001 : max_sar);
//--- :>@@5:B8@C5< 7=0G5=8O A8<2>;0 8 B09<D@59<0
ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe);
string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name);
//--- A>740Q< EM=4; 8=48:0B>@0
ResetLastError();
int handle=iSAR(symbol, period, step, max);
//--- ?@8 >H81:5 A>740=8O 8=48:0B>@0 2K2>48< A>>1I5=85 >1 >H81:5 2 6C@=0;
if(handle==INVALID_HANDLE)
{
PrintFormat("Failed to create iSAR(%s, %s, %.3f, %.2f) handle. Error %d",
symbol, TimeframeDescription(period), step, max, GetLastError());
}
//--- 2>72@0I05< @57C;LB0B A>740=8O EM=4;0 8=48:0B>@0
return handle;
}
//+------------------------------------------------------------------+
//| !>740QB 8 2>72@0I05B EM=4; 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)
{
//--- CAB0=02;8205< ?0@0<5B@K 8=48:0B>@0 2 4>?CAB8<KE ?@545;0E
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);
//--- :>@@5:B8@C5< 7=0G5=8O A8<2>;0 8 B09<D@59<0
ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe);
string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name);
//--- A>740Q< EM=4; 8=48:0B>@0
::ResetLastError();
int handle=::iAMA(symbol, period, ma_period, fast_ema, slow_ema, shift, price);
//--- ?@8 >H81:5 A>740=8O 8=48:0B>@0 2K2>48< A>>1I5=85 >1 >H81:5 2 6C@=0;
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());
}
//--- 2>72@0I05< @57C;LB0B A>740=8O EM=4;0 8=48:0B>@0
return handle;
}
//+------------------------------------------------------------------+
//| !>740QB 8 2>72@0I05B EM=4; 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)
{
//--- CAB0=02;8205< ?0@0<5B@K 8=48:0B>@0 2 4>?CAB8<KE ?@545;0E
int ma_period=(dema_period<1 ? 14 : dema_period);
//--- :>@@5:B8@C5< 7=0G5=8O A8<2>;0 8 B09<D@59<0
ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe);
string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name);
//--- A>740Q< EM=4; 8=48:0B>@0
::ResetLastError();
int handle=::iDEMA(symbol, period, ma_period, shift, price);
//--- ?@8 >H81:5 A>740=8O 8=48:0B>@0 2K2>48< A>>1I5=85 >1 >H81:5 2 6C@=0;
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());
}
//--- 2>72@0I05< @57C;LB0B A>740=8O EM=4;0 8=48:0B>@0
return handle;
}
//+------------------------------------------------------------------+
//| !>740QB 8 2>72@0I05B EM=4; 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)
{
//--- CAB0=02;8205< ?0@0<5B@K 8=48:0B>@0 2 4>?CAB8<KE ?@545;0E
int ma_period=(frama_period<1 ? 14 : frama_period);
//--- :>@@5:B8@C5< 7=0G5=8O A8<2>;0 8 B09<D@59<0
ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe);
string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name);
//--- A>740Q< EM=4; 8=48:0B>@0
::ResetLastError();
int handle=::iFrAMA(symbol, period, ma_period, shift, price);
//--- ?@8 >H81:5 A>740=8O 8=48:0B>@0 2K2>48< A>>1I5=85 >1 >H81:5 2 6C@=0;
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());
}
//--- 2>72@0I05< @57C;LB0B A>740=8O EM=4;0 8=48:0B>@0
return handle;
}
//+------------------------------------------------------------------+
//| !>740QB 8 2>72@0I05B EM=4; 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)
{
//--- CAB0=02;8205< ?0@0<5B@K 8=48:0B>@0 2 4>?CAB8<KE ?@545;0E
int ma_period=(period_ma<1 ? 14 : period_ma);
//--- :>@@5:B8@C5< 7=0G5=8O A8<2>;0 8 B09<D@59<0
ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe);
string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name);
//--- A>740Q< EM=4; 8=48:0B>@0
::ResetLastError();
int handle=::iMA(symbol, period, ma_period, shift, method, price);
//--- ?@8 >H81:5 A>740=8O 8=48:0B>@0 2K2>48< A>>1I5=85 >1 >H81:5 2 6C@=0;
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());
}
//--- 2>72@0I05< @57C;LB0B A>740=8O EM=4;0 8=48:0B>@0
return handle;
}
//+------------------------------------------------------------------+
//| !>740QB 8 2>72@0I05B EM=4; 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)
{
//--- CAB0=02;8205< ?0@0<5B@K 8=48:0B>@0 2 4>?CAB8<KE ?@545;0E
int ma_period=(tema_period<1 ? 14 : tema_period);
//--- :>@@5:B8@C5< 7=0G5=8O A8<2>;0 8 B09<D@59<0
ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe);
string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name);
//--- A>740Q< EM=4; 8=48:0B>@0
::ResetLastError();
int handle=::iTEMA(symbol, period, ma_period, shift, price);
//--- ?@8 >H81:5 A>740=8O 8=48:0B>@0 2K2>48< A>>1I5=85 >1 >H81:5 2 6C@=0;
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());
}
//--- 2>72@0I05< @57C;LB0B A>740=8O EM=4;0 8=48:0B>@0
return handle;
}
//+------------------------------------------------------------------+
//| !>740QB 8 2>72@0I05B EM=4; 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)
{
//--- CAB0=02;8205< ?0@0<5B@K 8=48:0B>@0 2 4>?CAB8<KE ?@545;0E
int ma_period =(period_cmo<1 ? 9 : period_cmo);
int ema_period=(period_ema<1 ? 12 : period_ema);
//--- :>@@5:B8@C5< 7=0G5=8O A8<2>;0 8 B09<D@59<0
ENUM_TIMEFRAMES period=(timeframe==PERIOD_CURRENT ? Period() : timeframe);
string symbol=(symbol_name==NULL || symbol_name=="" ? Symbol() : symbol_name);
//--- A>740Q< EM=4; 8=48:0B>@0
::ResetLastError();
int handle=::iVIDyA(symbol, period, ma_period, ema_period, shift, price);
//--- ?@8 >H81:5 A>740=8O 8=48:0B>@0 2K2>48< A>>1I5=85 >1 >H81:5 2 6C@=0;
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());
}
//--- 2>72@0I05< @57C;LB0B A>740=8O EM=4;0 8=48:0B>@0
return handle;
}
//+------------------------------------------------------------------+