MQL5Book/Include/TrailingStop.mqh

140 lines
4.2 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 16:09:41 +02:00
//+------------------------------------------------------------------+
//| TrailingStop.mqh |
//| Copyright 2022, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#include "MqlTradeSync.mqh"
//+------------------------------------------------------------------+
//| Base class for trailing on specific distance in points |
//+------------------------------------------------------------------+
class TrailingStop
{
const ulong ticket; // position to manage
const string symbol; // symbol of the position
const double point; // price point for the symbol
const uint distance; // stop loss in points (for default trailing method)
const uint step; // sensitivity in points (1+)
protected:
double level;
bool ok;
virtual double detectLevel() // override this method in descendants
{
return DBL_MAX; // default distance trailing is enabled
}
public:
TrailingStop(const ulong t, const uint d, const uint s = 1) :
ticket(t), distance(d), step(fmax(s, 1)),
symbol(PositionSelectByTicket(t) ? PositionGetString(POSITION_SYMBOL) : NULL),
point(SymbolInfoDouble(symbol, SYMBOL_POINT))
{
if(symbol == NULL)
{
WARNING("Position not found: " + (string)t);
ok = false;
}
else
{
ok = true;
}
}
bool isOK() const
{
return ok;
}
double getLevel() const
{
return level;
}
virtual bool trail()
{
if(!PositionSelectByTicket(ticket))
{
ok = false;
return false;
}
// get prices required for calculations
const double current = PositionGetDouble(POSITION_PRICE_CURRENT);
const double sl = PositionGetDouble(POSITION_SL);
const double tp = PositionGetDouble(POSITION_TP);
// POSITION_TYPE_BUY = 0 (false)
// POSITION_TYPE_SELL = 1 (true)
const bool sell = (bool)PositionGetInteger(POSITION_TYPE);
TU::TradeDirection dir(sell);
level = detectLevel();
if(level == 0) return true; // can't trail - caller should remove SL if they want
if(level == DBL_MAX) level = dir.negative(current, point * distance);
level = TU::NormalizePrice(level, symbol);
if(!dir.better(current, level))
{
return true; // can't change SL to profitable side
}
if(sl == 0)
{
PrintFormat("Initial SL: %f", level);
move(level, tp);
}
else
{
if(dir.better(level, sl) && fabs(level - sl) >= point * step)
{
PrintFormat("SL: %f -> %f", sl, level);
move(level, tp);
}
}
return true; // has position
}
bool move(const double sl, const double tp)
{
MqlTradeRequestSync request;
request.position = ticket;
if(request.adjust(sl, tp) && request.completed())
{
Print("OK Trailing: ", TU::StringOf(sl));
return true;
}
return false;
}
};
//+------------------------------------------------------------------+
//| Derived class for trailing by Moving Average |
//+------------------------------------------------------------------+
class TrailingStopByMA: public TrailingStop
{
int handle;
public:
TrailingStopByMA(const ulong t, const int period,
const int offset = 1,
const ENUM_MA_METHOD method = MODE_SMA,
const ENUM_APPLIED_PRICE type = PRICE_CLOSE): TrailingStop(t, 0, 1)
{
handle = iMA(_Symbol, PERIOD_CURRENT, period, offset, method, type);
}
virtual double detectLevel() override
{
double array[1];
ResetLastError();
if(CopyBuffer(handle, 0, 0, 1, array) != 1)
{
Print("CopyBuffer error: ", _LastError);
return 0;
}
return array[0];
}
};
//+------------------------------------------------------------------+