
//+------------------------------------------------------------------+
//| StopLossManager.mqh |
//| Copyright 2026, Algosphere |
//| https://algosphere-quant.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Algosphere"
#property link "https://algosphere-quant.com"
#property version "1.00"
#property strict
//+------------------------------------------------------------------+
//| Enumeration of stop loss calculation methods |
//+------------------------------------------------------------------+
enum ENUM_SL_METHOD
{
SL_METHOD_FIXED_PIPS=0, // Fixed Pips
SL_METHOD_ATR=1, // ATR Based
SL_METHOD_SWING=2, // Swing High/Low
SL_METHOD_PERCENT=3 // Percentage of Price
};
//+------------------------------------------------------------------+
//| Enumeration of trailing stop methods |
//+------------------------------------------------------------------+
enum ENUM_TRAIL_METHOD
{
TRAIL_NONE=0, // No Trailing
TRAIL_FIXED=1, // Fixed Distance
TRAIL_ATR=2, // ATR Based
TRAIL_STEP=3, // Step Trailing
TRAIL_BREAKEVEN=4 // Breakeven Only
};
//+------------------------------------------------------------------+
//| Structure for stop loss calculation result |
//+------------------------------------------------------------------+
struct SStopLossResult
{
double price; // Stop loss price level
double distance_points; // Distance in points
double distance_pips; // Distance in pips
bool valid; // Calculation valid flag
string error; // Error message if invalid
};
//+------------------------------------------------------------------+
//| Class for managing stop loss calculations and trailing |
//+------------------------------------------------------------------+
class CStopLossManager
{
private:
string m_symbol; // Trading symbol
ENUM_TIMEFRAMES m_timeframe; // Calculation timeframe
int m_atr_handle; // ATR indicator handle
int m_atr_period; // ATR period
double m_point; // Symbol point size
double m_pip_size; // Pip size in points
int m_digits; // Symbol digits
int m_stops_level; // Broker minimum stops level
bool m_initialized; // Initialization flag
//--- Private methods
double GetATRValue(const int shift=0);
double GetSwingHigh(const int lookback);
double GetSwingLow(const int lookback);
double NormalizePrice(const double price);
double AdjustForStopsLevel(const double sl_price,
const double entry_price,
const bool is_buy);
public:
//--- Constructor and destructor
CStopLossManager(void);
~CStopLossManager(void);
//--- Initialization
bool Init(const string symbol,
const ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT,
const int atr_period=14);
//--- Stop loss calculation methods
SStopLossResult CalculateStopLoss(const ENUM_SL_METHOD method,
const bool is_buy,
const double entry_price,
const double param1=0.0,
const double param2=0.0);
SStopLossResult CalculateFixedPipsSL(const bool is_buy,
const double entry_price,
const double pips);
SStopLossResult CalculateATRSL(const bool is_buy,
const double entry_price,
const double atr_multiplier=1.5);
SStopLossResult CalculateSwingSL(const bool is_buy,
const double entry_price,
const int lookback=10,
const double buffer_pips=5.0);
SStopLossResult CalculatePercentSL(const bool is_buy,
const double entry_price,
const double percent=1.0);
//--- Take profit calculation
double CalculateTakeProfit(const bool is_buy,
const double entry_price,
const double sl_price,
const double rr_ratio=2.0);
//--- Trailing stop methods
double CalculateTrailingStop(const ENUM_TRAIL_METHOD method,
const bool is_buy,
const double entry_price,
const double current_sl,
const double current_price,
const double param1=0.0,
const double param2=0.0);
double TrailFixed(const bool is_buy,
const double current_sl,
const double current_price,
const double trail_distance_pips);
double TrailATR(const bool is_buy,
const double current_sl,
const double current_price,
const double atr_multiplier=2.0);
double TrailStep(const bool is_buy,
const double entry_price,
const double current_sl,
const double current_price,
const double step_pips=10.0);
double TrailBreakeven(const bool is_buy,
const double entry_price,
const double current_sl,
const double current_price,
const double trigger_pips=20.0,
const double be_offset_pips=2.0);
//--- Utility methods
double PipsToPoints(const double pips);
double PointsToPips(const double points);
double PipsToPrice(const double pips);
//--- Information methods
double GetCurrentATR(void) { return(GetATRValue(0)); }
int GetStopsLevel(void) { return(m_stops_level); }
double GetPipSize(void) { return(m_pip_size); }
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CStopLossManager::CStopLossManager(void) : m_symbol(""),
m_timeframe(PERIOD_CURRENT),
m_atr_handle(INVALID_HANDLE),
m_atr_period(14),
m_point(0),
m_pip_size(0),
m_digits(0),
m_stops_level(0),
m_initialized(false)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CStopLossManager::~CStopLossManager(void)
{
//--- Release indicator handle
if(m_atr_handle!=INVALID_HANDLE)
IndicatorRelease(m_atr_handle);
}
//+------------------------------------------------------------------+
//| Initialize the stop loss manager |
//+------------------------------------------------------------------+
bool CStopLossManager::Init(const string symbol,
const ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT,
const int atr_period=14)
{
m_symbol=symbol;
m_timeframe=(timeframe==PERIOD_CURRENT) ? Period() : timeframe;
m_atr_period=atr_period;
//--- Get symbol specifications
m_point=SymbolInfoDouble(symbol,SYMBOL_POINT);
m_digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
m_stops_level=(int)SymbolInfoInteger(symbol,SYMBOL_TRADE_STOPS_LEVEL);
//--- Calculate pip size (10 points for 5-digit, 1 point for 4-digit)
m_pip_size=(m_digits==5 || m_digits==3) ? m_point*10 : m_point;
//--- Validate symbol data
if(m_point==0)
{
Print("Error: Invalid symbol point value for ",symbol);
return(false);
}
//--- Create ATR indicator handle
m_atr_handle=iATR(symbol,m_timeframe,m_atr_period);
if(m_atr_handle==INVALID_HANDLE)
{
Print("Error: Failed to create ATR indicator handle");
return(false);
}
m_initialized=true;
return(true);
}
//+------------------------------------------------------------------+
//| Get ATR indicator value |
//+------------------------------------------------------------------+
double CStopLossManager::GetATRValue(const int shift=0)
{
if(m_atr_handle==INVALID_HANDLE)
return(0.0);
double atr_buffer[1];
if(CopyBuffer(m_atr_handle,0,shift,1,atr_buffer)!=1)
return(0.0);
return(atr_buffer[0]);
}
//+------------------------------------------------------------------+
//| Get swing high price |
//+------------------------------------------------------------------+
double CStopLossManager::GetSwingHigh(const int lookback)
{
double high_buffer[];
ArraySetAsSeries(high_buffer,true);
if(CopyHigh(m_symbol,m_timeframe,0,lookback,high_buffer)!=lookback)
return(0.0);
double highest=high_buffer[0];
for(int i=1; i<lookback; i++)
{
if(high_buffer[i]>highest)
highest=high_buffer[i];
}
return(highest);
}
//+------------------------------------------------------------------+
//| Get swing low price |
//+------------------------------------------------------------------+
double CStopLossManager::GetSwingLow(const int lookback)
{
double low_buffer[];
ArraySetAsSeries(low_buffer,true);
if(CopyLow(m_symbol,m_timeframe,0,lookback,low_buffer)!=lookback)
return(0.0);
double lowest=low_buffer[0];
for(int i=1; i<lookback; i++)
{
if(low_buffer[i]<lowest)
lowest=low_buffer[i];
}
return(lowest);
}
//+------------------------------------------------------------------+
//| Normalize price to symbol digits |
//+------------------------------------------------------------------+
double CStopLossManager::NormalizePrice(const double price)
{
return(NormalizeDouble(price,m_digits));
}
//+------------------------------------------------------------------+
//| Adjust stop loss for broker minimum stops level |
//+------------------------------------------------------------------+
double CStopLossManager::AdjustForStopsLevel(const double sl_price,
const double entry_price,
const bool is_buy)
{
if(m_stops_level==0)
return(sl_price);
double min_distance=m_stops_level*m_point;
double current_distance=MathAbs(entry_price-sl_price);
if(current_distance>=min_distance)
return(sl_price);
//--- Adjust stop loss to meet minimum distance
if(is_buy)
return(NormalizePrice(entry_price-min_distance));
else
return(NormalizePrice(entry_price+min_distance));
}
//+------------------------------------------------------------------+
//| Universal stop loss calculation method |
//+------------------------------------------------------------------+
SStopLossResult CStopLossManager::CalculateStopLoss(const ENUM_SL_METHOD method,
const bool is_buy,
const double entry_price,
const double param1=0.0,
const double param2=0.0)
{
SStopLossResult result;
ZeroMemory(result);
if(!m_initialized)
{
result.valid=false;
result.error="Stop loss manager not initialized";
return(result);
}
switch(method)
{
case SL_METHOD_FIXED_PIPS:
result=CalculateFixedPipsSL(is_buy,entry_price,param1);
break;
case SL_METHOD_ATR:
result=CalculateATRSL(is_buy,entry_price,param1>0 ? param1 : 1.5);
break;
case SL_METHOD_SWING:
result=CalculateSwingSL(is_buy,entry_price,(int)(param1>0 ? param1 : 10),param2>0 ? param2 : 5.0);
break;
case SL_METHOD_PERCENT:
result=CalculatePercentSL(is_buy,entry_price,param1>0 ? param1 : 1.0);
break;
}
return(result);
}
//+------------------------------------------------------------------+
//| Calculate fixed pips stop loss |
//+------------------------------------------------------------------+
SStopLossResult CStopLossManager::CalculateFixedPipsSL(const bool is_buy,
const double entry_price,
const double pips)
{
SStopLossResult result;
ZeroMemory(result);
if(pips<=0)
{
result.valid=false;
result.error="Invalid pips value";
return(result);
}
double distance=pips*m_pip_size;
if(is_buy)
result.price=NormalizePrice(entry_price-distance);
else
result.price=NormalizePrice(entry_price+distance);
//--- Adjust for stops level
result.price=AdjustForStopsLevel(result.price,entry_price,is_buy);
//--- Calculate final distances
result.distance_points=MathAbs(entry_price-result.price)/m_point;
result.distance_pips=result.distance_points/(m_pip_size/m_point);
result.valid=true;
return(result);
}
//+------------------------------------------------------------------+
//| Calculate ATR-based stop loss |
//+------------------------------------------------------------------+
SStopLossResult CStopLossManager::CalculateATRSL(const bool is_buy,
const double entry_price,
const double atr_multiplier=1.5)
{
SStopLossResult result;
ZeroMemory(result);
double atr=GetATRValue(0);
if(atr==0)
{
result.valid=false;
result.error="Failed to get ATR value";
return(result);
}
double distance=atr*atr_multiplier;
if(is_buy)
result.price=NormalizePrice(entry_price-distance);
else
result.price=NormalizePrice(entry_price+distance);
//--- Adjust for stops level
result.price=AdjustForStopsLevel(result.price,entry_price,is_buy);
//--- Calculate final distances
result.distance_points=MathAbs(entry_price-result.price)/m_point;
result.distance_pips=result.distance_points/(m_pip_size/m_point);
result.valid=true;
return(result);
}
//+------------------------------------------------------------------+
//| Calculate swing-based stop loss |
//+------------------------------------------------------------------+
SStopLossResult CStopLossManager::CalculateSwingSL(const bool is_buy,
const double entry_price,
const int lookback=10,
const double buffer_pips=5.0)
{
SStopLossResult result;
ZeroMemory(result);
double buffer=buffer_pips*m_pip_size;
if(is_buy)
{
double swing_low=GetSwingLow(lookback);
if(swing_low==0)
{
result.valid=false;
result.error="Failed to find swing low";
return(result);
}
result.price=NormalizePrice(swing_low-buffer);
}
else
{
double swing_high=GetSwingHigh(lookback);
if(swing_high==0)
{
result.valid=false;
result.error="Failed to find swing high";
return(result);
}
result.price=NormalizePrice(swing_high+buffer);
}
//--- Adjust for stops level
result.price=AdjustForStopsLevel(result.price,entry_price,is_buy);
//--- Calculate final distances
result.distance_points=MathAbs(entry_price-result.price)/m_point;
result.distance_pips=result.distance_points/(m_pip_size/m_point);
result.valid=true;
return(result);
}
//+------------------------------------------------------------------+
//| Calculate percentage-based stop loss |
//+------------------------------------------------------------------+
SStopLossResult CStopLossManager::CalculatePercentSL(const bool is_buy,
const double entry_price,
const double percent=1.0)
{
SStopLossResult result;
ZeroMemory(result);
if(percent<=0 || percent>100)
{
result.valid=false;
result.error="Invalid percentage value";
return(result);
}
double distance=entry_price*(percent/100.0);
if(is_buy)
result.price=NormalizePrice(entry_price-distance);
else
result.price=NormalizePrice(entry_price+distance);
//--- Adjust for stops level
result.price=AdjustForStopsLevel(result.price,entry_price,is_buy);
//--- Calculate final distances
result.distance_points=MathAbs(entry_price-result.price)/m_point;
result.distance_pips=result.distance_points/(m_pip_size/m_point);
result.valid=true;
return(result);
}
//+------------------------------------------------------------------+
//| Calculate take profit based on risk:reward ratio |
//+------------------------------------------------------------------+
double CStopLossManager::CalculateTakeProfit(const bool is_buy,
const double entry_price,
const double sl_price,
const double rr_ratio=2.0)
{
double sl_distance=MathAbs(entry_price-sl_price);
double tp_distance=sl_distance*rr_ratio;
if(is_buy)
return(NormalizePrice(entry_price+tp_distance));
else
return(NormalizePrice(entry_price-tp_distance));
}
//+------------------------------------------------------------------+
//| Universal trailing stop calculation |
//+------------------------------------------------------------------+
double CStopLossManager::CalculateTrailingStop(const ENUM_TRAIL_METHOD method,
const bool is_buy,
const double entry_price,
const double current_sl,
const double current_price,
const double param1=0.0,
const double param2=0.0)
{
if(!m_initialized)
return(current_sl);
switch(method)
{
case TRAIL_NONE:
return(current_sl);
case TRAIL_FIXED:
return(TrailFixed(is_buy,current_sl,current_price,param1>0 ? param1 : 20.0));
case TRAIL_ATR:
return(TrailATR(is_buy,current_sl,current_price,param1>0 ? param1 : 2.0));
case TRAIL_STEP:
return(TrailStep(is_buy,entry_price,current_sl,current_price,param1>0 ? param1 : 10.0));
case TRAIL_BREAKEVEN:
return(TrailBreakeven(is_buy,entry_price,current_sl,current_price,
param1>0 ? param1 : 20.0,param2>0 ? param2 : 2.0));
}
return(current_sl);
}
//+------------------------------------------------------------------+
//| Fixed distance trailing stop |
//+------------------------------------------------------------------+
double CStopLossManager::TrailFixed(const bool is_buy,
const double current_sl,
const double current_price,
const double trail_distance_pips)
{
double distance=trail_distance_pips*m_pip_size;
double new_sl;
if(is_buy)
{
new_sl=NormalizePrice(current_price-distance);
if(new_sl>current_sl)
return(new_sl);
}
else
{
new_sl=NormalizePrice(current_price+distance);
if(new_sl<current_sl || current_sl==0)
return(new_sl);
}
return(current_sl);
}
//+------------------------------------------------------------------+
//| ATR-based trailing stop |
//+------------------------------------------------------------------+
double CStopLossManager::TrailATR(const bool is_buy,
const double current_sl,
const double current_price,
const double atr_multiplier=2.0)
{
double atr=GetATRValue(0);
if(atr==0)
return(current_sl);
double distance=atr*atr_multiplier;
double new_sl;
if(is_buy)
{
new_sl=NormalizePrice(current_price-distance);
if(new_sl>current_sl)
return(new_sl);
}
else
{
new_sl=NormalizePrice(current_price+distance);
if(new_sl<current_sl || current_sl==0)
return(new_sl);
}
return(current_sl);
}
//+------------------------------------------------------------------+
//| Step trailing stop |
//+------------------------------------------------------------------+
double CStopLossManager::TrailStep(const bool is_buy,
const double entry_price,
const double current_sl,
const double current_price,
const double step_pips=10.0)
{
double step=step_pips*m_pip_size;
double profit_distance;
int steps_in_profit;
if(is_buy)
{
profit_distance=current_price-entry_price;
if(profit_distance<=0)
return(current_sl);
steps_in_profit=(int)MathFloor(profit_distance/step);
if(steps_in_profit>0)
{
double new_sl=NormalizePrice(entry_price+(steps_in_profit-1)*step);
if(new_sl>current_sl)
return(new_sl);
}
}
else
{
profit_distance=entry_price-current_price;
if(profit_distance<=0)
return(current_sl);
steps_in_profit=(int)MathFloor(profit_distance/step);
if(steps_in_profit>0)
{
double new_sl=NormalizePrice(entry_price-(steps_in_profit-1)*step);
if(new_sl<current_sl || current_sl==0)
return(new_sl);
}
}
return(current_sl);
}
//+------------------------------------------------------------------+
//| Breakeven trailing stop |
//+------------------------------------------------------------------+
double CStopLossManager::TrailBreakeven(const bool is_buy,
const double entry_price,
const double current_sl,
const double current_price,
const double trigger_pips=20.0,
const double be_offset_pips=2.0)
{
double trigger=trigger_pips*m_pip_size;
double offset=be_offset_pips*m_pip_size;
if(is_buy)
{
//--- Check if price has moved enough to trigger breakeven
if(current_price-entry_price>=trigger)
{
double be_level=NormalizePrice(entry_price+offset);
if(be_level>current_sl)
return(be_level);
}
}
else
{
//--- Check if price has moved enough to trigger breakeven
if(entry_price-current_price>=trigger)
{
double be_level=NormalizePrice(entry_price-offset);
if(be_level<current_sl || current_sl==0)
return(be_level);
}
}
return(current_sl);
}
//+------------------------------------------------------------------+
//| Convert pips to points |
//+------------------------------------------------------------------+
double CStopLossManager::PipsToPoints(const double pips)
{
return(pips*(m_pip_size/m_point));
}
//+------------------------------------------------------------------+
//| Convert points to pips |
//+------------------------------------------------------------------+
double CStopLossManager::PointsToPips(const double points)
{
if(m_pip_size==0)
return(0);
return(points/(m_pip_size/m_point));
}
//+------------------------------------------------------------------+
//| Convert pips to price distance |
//+------------------------------------------------------------------+
double CStopLossManager::PipsToPrice(const double pips)
{
return(pips*m_pip_size);
}
//+------------------------------------------------------------------+