Files
2026-05-27 12:32:26 -04:00

184 lines
5.7 KiB
Plaintext

//+------------------------------------------------------------------+
//| TrailingStop.mqh - Trailing stop and break-even management |
//| Modifies SL based on profit targets |
//+------------------------------------------------------------------+
#ifndef __TRAILINGSTOP_MQH__
#define __TRAILINGSTOP_MQH__
#include "Config.mqh"
#include "Logger.mqh"
#include "MarketData.mqh"
#include "Utilities.mqh"
#include "TradeManager.mqh"
class CTrailingStop
{
private:
CMarketData *mp_market_data;
CLogger *mp_logger;
CTradeManager *mp_trade_manager;
public:
// Constructor
CTrailingStop(CMarketData *market_data, CLogger *logger, CTradeManager *trade_manager)
{
mp_market_data = market_data;
mp_logger = logger;
mp_trade_manager = trade_manager;
}
// Update trailing stop for all positions
void UpdateAllPositions()
{
if(!g_use_trailing_stop && !g_use_break_even)
return;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionSelectByTicket(PositionGetTicket(i)))
{
if(PositionGetString(POSITION_SYMBOL) == mp_market_data.GetSymbol() &&
PositionGetInteger(POSITION_MAGIC) == g_magic_number)
{
UpdatePosition(PositionGetTicket(i));
}
}
}
}
// Update trailing stop for a single position
void UpdatePosition(ulong ticket)
{
if(ticket == 0)
return;
if(!PositionSelectByTicket(ticket))
return;
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double current_sl = PositionGetDouble(POSITION_SL);
double current_tp = PositionGetDouble(POSITION_TP);
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double profit = PositionGetDouble(POSITION_PROFIT);
double new_sl = current_sl;
double new_tp = current_tp;
bool need_modify = false;
// Apply break-even logic first
if(g_use_break_even)
{
if(ApplyBreakEven(pos_type, open_price, current_sl, profit, new_sl))
{
need_modify = true;
}
}
// Apply trailing stop logic
if(g_use_trailing_stop)
{
if(ApplyTrailingStop(pos_type, current_sl, new_sl))
{
need_modify = true;
}
}
// Only modify if SL changed
if(need_modify && new_sl != current_sl)
{
mp_trade_manager.ModifyPosition(ticket, new_sl, new_tp);
}
}
private:
// Apply break-even logic
bool ApplyBreakEven(ENUM_POSITION_TYPE pos_type, double open_price,
double current_sl, double profit, double &new_sl)
{
double break_even_trigger = CUtilities::PointsToPrice(mp_market_data.GetSymbol(),
g_break_even_profit);
double break_even_distance = CUtilities::PointsToPrice(mp_market_data.GetSymbol(),
g_break_even_sl);
// Break-even only if profit threshold is reached
if(profit < break_even_trigger)
return false;
// For BUY: move SL to BE (open price + distance)
if(pos_type == POSITION_TYPE_BUY)
{
double be_sl = open_price + break_even_distance;
be_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), be_sl);
if(be_sl > current_sl)
{
new_sl = be_sl;
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("Break-even applied: BUY SL moved to %.5f", be_sl));
return true;
}
}
// For SELL: move SL to BE (open price - distance)
else if(pos_type == POSITION_TYPE_SELL)
{
double be_sl = open_price - break_even_distance;
be_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), be_sl);
if(be_sl < current_sl)
{
new_sl = be_sl;
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("Break-even applied: SELL SL moved to %.5f", be_sl));
return true;
}
}
return false;
}
// Apply trailing stop logic
bool ApplyTrailingStop(ENUM_POSITION_TYPE pos_type, double current_sl, double &new_sl)
{
double bid = mp_market_data.GetBid();
double ask = mp_market_data.GetAsk();
double trail_distance = CUtilities::PointsToPrice(mp_market_data.GetSymbol(),
g_trailing_stop_points);
// For BUY: trailing stop follows price from below
if(pos_type == POSITION_TYPE_BUY)
{
double candidate_sl = bid - trail_distance;
candidate_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), candidate_sl);
// Only move SL up (never down)
if(candidate_sl > current_sl)
{
new_sl = candidate_sl;
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("Trailing stop applied: BUY SL moved to %.5f", candidate_sl));
return true;
}
}
// For SELL: trailing stop follows price from above
else if(pos_type == POSITION_TYPE_SELL)
{
double candidate_sl = ask + trail_distance;
candidate_sl = CUtilities::NormalizePrice(mp_market_data.GetSymbol(), candidate_sl);
// Only move SL down (never up)
if(candidate_sl < current_sl)
{
new_sl = candidate_sl;
if(mp_logger && g_debug_mode)
mp_logger.Info(StringFormat("Trailing stop applied: SELL SL moved to %.5f", candidate_sl));
return true;
}
}
return false;
}
};
#endif //__TRAILINGSTOP_MQH__