ATR Based Stop Loss Manager
//+------------------------------------------------------------------+ //| StopLossManager.mqh | //+------------------------------------------------------------------+ #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 //+------------------------------------------------------------------+ //| 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); //+------------------------------------------------------------------+